diff --git a/app/assets/javascripts/datatables/jquery.dataTables.min.js b/app/assets/javascripts/datatables/jquery.dataTables.min.js new file mode 100644 index 0000000000..3248d74907 --- /dev/null +++ b/app/assets/javascripts/datatables/jquery.dataTables.min.js @@ -0,0 +1,155 @@ +/*! DataTables 1.10.2 + * ©2008-2014 SpryMedia Ltd - datatables.net/license + */ +(function(za,O,l){var N=function(h){function T(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),d[c]=e,"o"===b[1]&&T(a[e])});a._hungarianMap=d}function G(a,b,c){a._hungarianMap||T(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==l&&(c||b[d]===l))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),G(a[d],b[d],c)):b[d]=b[e]})}function N(a){var b=p.defaults.oLanguage,c=a.sZeroRecords; +!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&D(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&D(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&cb(a)}function db(a){w(a,"ordering","bSort");w(a,"orderMulti","bSortMulti");w(a,"orderClasses","bSortClasses");w(a,"orderCellsTop","bSortCellsTop");w(a,"order","aaSorting");w(a,"orderFixed","aaSortingFixed");w(a,"paging","bPaginate"); +w(a,"pagingType","sPaginationType");w(a,"pageLength","iDisplayLength");w(a,"searching","bFilter");if(a=a.aoSearchCols)for(var b=0,c=a.length;b").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("
").css({position:"absolute",top:1,left:1,width:100, +overflow:"scroll"}).append(h('
').css({width:"100%",height:10}))).appendTo("body"),c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==c.offset().left;b.remove()}function gb(a,b,c,d,e,f){var g,j=!1;c!==l&&(g=c,j=!0);for(;d!==e;)a.hasOwnProperty(d)&&(g=j?b(g,a[d],d,a):a[d],j=!0,d+=f);return g}function Aa(a,b){var c=p.defaults.column,d=a.aoColumns.length,c=h.extend({},p.models.oColumn,c,{nTh:b?b:O.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML: +"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},p.models.oSearch,c[d]);fa(a,d,null)}function fa(a,b,c){var b=a.aoColumns[b],d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==l&&null!==c&&(eb(c),G(p.defaults.column,c),c.mDataProp!==l&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&& +!c.sClass&&(c.sClass=c.className),h.extend(b,c),D(b,c,"sWidth","sWidthOrig"),"number"===typeof c.iDataSort&&(b.aDataSort=[c.iDataSort]),D(b,c,"aDataSort"));var g=b.mData,j=U(g),i=b.mRender?U(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var d=j(a,b,l,c);return i&&b?i(d,b,a,c):d};b.fnSetData=function(a,b,c){return Ba(g)(a,b,c)};a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone)); +a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function V(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ca(a);for(var c=0,d=b.length;co[f])d(m.length+ +o[f],n);else if("string"===typeof o[f]){j=0;for(i=m.length;jb&&a[e]--; -1!=d&&c===l&&a.splice(d,1)}function la(a,b,c,d){var e=a.aoData[b],f;if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData= +ia(a,e).data;else{var g=e.anCells,j;if(g){c=0;for(f=g.length;c").appendTo(g));b=0;for(c=m.length;btr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(n.sHeaderTH);h(j).find(">tr>th, >tr>td").addClass(n.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,n=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,B(a,!1);else if(j){if(!a.bDestroying&&!jb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:n;for(j=j?0:g;j",{"class":e?d[0]:""}).append(h("",{valign:"top",colSpan:Z(a),"class":a.oClasses.sRowEmpty}).html(c))[0];u(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ha(a),g,n,i]);u(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0], +Ha(a),g,n,i]);d=h(a.nTBody);d.children().detach();d.append(h(b));u(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function L(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&kb(a);d?ca(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;K(a);a._drawHold=!1}function lb(a){var b=a.oClasses,c=h(a.nTable),c=h("
").insertBefore(c),d=a.oFeatures,e=h("
",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)}); +a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,n,m,o,k=0;k")[0];n=f[k+1];if("'"==n||'"'==n){m="";for(o=2;f[k+o]!=n;)m+=f[k+o],o++;"H"==m?m=b.sJUIHeader:"F"==m&&(m=b.sJUIFooter);-1!=m.indexOf(".")?(n=m.split("."),i.id=n[0].substr(1,n[0].length-1),i.className=n[1]):"#"==m.charAt(0)?i.id=m.substr(1,m.length-1):i.className=m;k+=o}e.append(i);e=h(i)}else if(">"==j)e=e.parent();else if("l"== +j&&d.bPaginate&&d.bLengthChange)g=mb(a);else if("f"==j&&d.bFilter)g=nb(a);else if("r"==j&&d.bProcessing)g=ob(a);else if("t"==j)g=pb(a);else if("i"==j&&d.bInfo)g=qb(a);else if("p"==j&&d.bPaginate)g=rb(a);else if(0!==p.ext.feature.length){i=p.ext.feature;o=0;for(n=i.length;o',j=d.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("
", +{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("
").addClass(b.sLength); +a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",i).val(a._iDisplayLength).bind("change.DT",function(){Pa(a,h(this).val());K(a)});h(a.nTable).bind("length.dt.DT",function(b,c,d){a===c&&h("select",i).val(d)});return i[0]}function rb(a){var b=a.sPaginationType,c=p.ext.pager[b],d="function"===typeof c,e=function(a){K(a)},b=h("
").addClass(a.oClasses.sPaging+b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+ +"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),m=-1===i,b=m?0:Math.ceil(b/i),i=m?1:Math.ceil(h/i),h=c(b,i),o,m=0;for(o=f.p.length;mf&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"== +b?d+e",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function B(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");u(a,null,"processing",[a,b])}function pb(a){var b=h(a.nTable);b.attr("role", +"grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),n=h(b[0].cloneNode(!1)),m=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");m.length||(m=null);c=h("
",{"class":f.sScrollWrapper}).append(h("
",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:s(d):"100%"}).append(h("
",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box", +width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append(b.children("thead")))).append("top"===j?g:null)).append(h("
",{"class":f.sScrollBody}).css({overflow:"auto",height:!e?null:s(e),width:!d?null:s(d)}).append(b));m&&c.append(h("
",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:s(d):"100%"}).append(h("
",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",0).append(b.children("tfoot")))).append("bottom"===j?g: +null));var b=c.children(),o=b[0],f=b[1],k=m?b[2]:null;d&&h(f).scroll(function(){var a=this.scrollLeft;o.scrollLeft=a;m&&(k.scrollLeft=a)});a.nScrollHead=o;a.nScrollBody=f;a.nScrollFoot=k;a.aoDrawCallback.push({fn:W,sName:"scrolling"});return c[0]}function W(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,f=b.iBarWidth,g=h(a.nScrollHead),j=g[0].style,i=g.children("div"),n=i[0].style,m=i.children("table"),i=a.nScrollBody,o=h(i),k=i.style,l=h(a.nScrollFoot).children("div"),p=l.children("table"),r=h(a.nTHead), +q=h(a.nTable),da=q[0],M=da.style,J=a.nTFoot?h(a.nTFoot):null,u=a.oBrowser,v=u.bScrollOversize,y,t,x,w,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};q.children("thead, tfoot").remove();z=r.clone().prependTo(q);y=r.find("tr");x=z.find("tr");z.find("th, td").removeAttr("tabindex");J&&(w=J.clone().prependTo(q),t=J.find("tr"),w=w.find("tr"));c||(k.width="100%",g[0].style.width="100%");h.each(ma(a,z),function(b,c){D= +ga(a,b);c.style.width=a.aoColumns[D].sWidth});J&&F(function(a){a.style.width=""},w);b.bCollapse&&""!==e&&(k.height=o[0].offsetHeight+r[0].offsetHeight+"px");g=q.outerWidth();if(""===c){if(M.width="100%",v&&(q.find("tbody").height()>i.offsetHeight||"scroll"==o.css("overflow-y")))M.width=s(q.outerWidth()-f)}else""!==d?M.width=s(d):g==o.width()&&o.height()g-f&&(M.width=s(g))):M.width=s(g);g=q.outerWidth();F(E,x);F(function(a){C.push(a.innerHTML);A.push(s(h(a).css("width")))}, +x);F(function(a,b){a.style.width=A[b]},y);h(x).height(0);J&&(F(E,w),F(function(a){B.push(s(h(a).css("width")))},w),F(function(a,b){a.style.width=B[b]},t),h(w).height(0));F(function(a,b){a.innerHTML='
'+C[b]+"
";a.style.width=A[b]},x);J&&F(function(a,b){a.innerHTML="";a.style.width=B[b]},w);if(q.outerWidth()i.offsetHeight||"scroll"==o.css("overflow-y")?g+f:g;if(v&&(i.scrollHeight>i.offsetHeight||"scroll"==o.css("overflow-y")))M.width= +s(t-f);(""===c||""!==d)&&P(a,1,"Possible column misalignment",6)}else t="100%";k.width=s(t);j.width=s(t);J&&(a.nScrollFoot.style.width=s(t));!e&&v&&(k.height=s(da.offsetHeight+f));e&&b.bCollapse&&(k.height=s(e),b=c&&da.offsetWidth>i.offsetWidth?f:0,da.offsetHeighti.clientHeight||"scroll"==o.css("overflow-y");u="padding"+(u.bScrollbarLeft?"Left":"Right");n[u]=m?f+"px":"0px";J&&(p[0].style.width= +s(b),l[0].style.width=s(b),l[0].style[u]=m?f+"px":"0px");o.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function F(a,b,c){for(var d=0,e=0,f=b.length,g,j;e"));j.find("tfoot th, tfoot td").css("width","");var p=j.find("tbody tr"),i=ma(a,j.find("thead")[0]);for(k=0;k").css("width",s(a)).appendTo(b||O.body),d=c[0].offsetWidth;c.remove();return d}function Eb(a,b){var c= +a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(h(b).outerWidth()-c)}function Db(a,b){var c=Fb(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?h("").html(A(a,c,b,"display"))[0]:d.anCells[b]}function Fb(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;fd&&(d=c.length,e=f);return e}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Gb(){if(!p.__scrollbarWidth){var a= +h("

").css({width:"100%",height:200,padding:0})[0],b=h("

").css({position:"absolute",top:0,left:0,width:200,height:150,padding:0,overflow:"hidden",visibility:"hidden"}).append(a).appendTo("body"),c=a.offsetWidth;b.css("overflow","scroll");a=a.offsetWidth;c===a&&(a=b[0].clientWidth);b.remove();p.__scrollbarWidth=c-a}return p.__scrollbarWidth}function R(a){var b,c,d=[],e=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var n=[];f=function(a){a.length&&!h.isArray(a[0])?n.push(a):n.push.apply(n, +a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;ae?1:0,0!==c)return"asc"===j.dir?c:-c;c=d[a];e=d[b];return ce?1:0}):i.sort(function(a,b){var c,g,j,i,l=h.length,p=f[a]._aSortData,r=f[b]._aSortData;for(j=0;jg?1:0})}a.bSorted=!0}function Ib(a){for(var b,c, +d=a.aoColumns,e=R(a),a=a.oLanguage.oAria,f=0,g=d.length;f/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0=f.length?0:b+1};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,C(e,"0")),-1!==c?(b=g(e[c]),e[c][1]=f[b],e[c]._idx=b):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);L(a);"function"==typeof d&&d(a)}function Ka(a,b,c,d){var e=a.aoColumns[c];Ta(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(B(a,!0),setTimeout(function(){Sa(a,c,b.shiftKey, +d);"ssp"!==z(a)&&B(a,!1)},0)):Sa(a,c,b.shiftKey,d))})}function sa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=R(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;ee?e+1:3));e=0;for(f=d.length;ee?e+1:3))}a.aLastSort=d}function Hb(a,b){var c=a.aoColumns[b],d=p.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,Y(a,b)));for(var f,g=p.ext.type.order[c.sType+ +"-pre"],j=0,i=a.aoData.length;j= +d.length?[0,c[1]]:c)});h.extend(a.oPreviousSearch,zb(e.search));b=0;for(c=e.columns.length;bb)b=0;a._iDisplayStart=b}function La(a,b){var c= +a.renderer,d=p.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===typeof c?d[c]||d._:d._}function z(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Ua(a,b){var c=[],c=Lb.numbers_length,d=Math.floor(c/2);b<=c?c=S(0,b):a<=d?(c=S(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=S(b-(c-2),b):(c=S(a-1,a+2),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function cb(a){h.each({num:function(b){return va(b, +a)},"num-fmt":function(b){return va(b,a,Va)},"html-num":function(b){return va(b,a,wa)},"html-num-fmt":function(b){return va(b,a,wa,Va)}},function(b,c){t.type.order[b+a+"-pre"]=c})}function Mb(a){return function(){var b=[ua(this[p.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return p.ext.internal[a].apply(this,b)}}var p,t,q,r,v,Wa={},Nb=/[\r\n]/g,wa=/<.*?>/g,Yb=/^[\w\+\-]/,Zb=/[\w\+\-]$/,Vb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Va=/[',$\u00a3\u20ac\u00a5%\u2009\u202F]/g, +H=function(a){return!a||!0===a||"-"===a?!0:!1},Ob=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Pb=function(a,b){Wa[b]||(Wa[b]=RegExp(Oa(b),"g"));return"string"===typeof a?a.replace(/\./g,"").replace(Wa[b],"."):a},Xa=function(a,b,c){var d="string"===typeof a;b&&d&&(a=Pb(a,b));c&&d&&(a=a.replace(Va,""));return H(a)||!isNaN(parseFloat(a))&&isFinite(a)},Qb=function(a,b,c){return H(a)?!0:!(H(a)||"string"===typeof a)?null:Xa(a.replace(wa,""),b,c)?!0:null},C=function(a,b,c){var d= +[],e=0,f=a.length;if(c!==l)for(;e")[0],Wb=qa.textContent!==l,Xb=/<.*?>/g;p=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new q(ua(this[t.iApiIndex])):new q(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===l||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing= +function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===l||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&W(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===l||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);(c===l||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(!a)}; +this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===l?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==l){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==l||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==l?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase(); +return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===l||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===l||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return ua(this[t.iApiIndex])}; +this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===l||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===l||e)&&h.columns.adjust();(d===l||d)&&h.draw();return 0};this.fnVersionCheck=t.fnVersionCheck;var b=this,c=a===l,d=this.length;c&&(a={});this.oApi=this.internal=t.internal;for(var e in p.ext.internal)e&&(this[e]=Mb(e));this.each(function(){var e={},g=1t<"F"ip>'),k.renderer)?h.isPlainObject(k.renderer)&&!k.renderer.header&&(k.renderer.header="jqueryui"):k.renderer="jqueryui":h.extend(n,p.ext.classes,g.oClasses);h(this).addClass(n.sTable);if(""!==k.oScroll.sX||""!==k.oScroll.sY)k.oScroll.iBarWidth=Gb();!0===k.oScroll.sX&&(k.oScroll.sX= +"100%");k.iInitDisplayStart===l&&(k.iInitDisplayStart=g.iDisplayStart,k._iDisplayStart=g.iDisplayStart);null!==g.iDeferLoading&&(k.bDeferLoading=!0,j=h.isArray(g.iDeferLoading),k._iRecordsDisplay=j?g.iDeferLoading[0]:g.iDeferLoading,k._iRecordsTotal=j?g.iDeferLoading[1]:g.iDeferLoading);""!==g.oLanguage.sUrl?(k.oLanguage.sUrl=g.oLanguage.sUrl,h.getJSON(k.oLanguage.sUrl,null,function(a){N(a);G(m.oLanguage,a);h.extend(true,k.oLanguage,g.oLanguage,a);ra(k)}),e=!0):h.extend(!0,k.oLanguage,g.oLanguage); +null===g.asStripeClasses&&(k.asStripeClasses=[n.sStripeOdd,n.sStripeEven]);var j=k.asStripeClasses,r=h("tbody tr:eq(0)",this);-1!==h.inArray(!0,h.map(j,function(a){return r.hasClass(a)}))&&(h("tbody tr",this).removeClass(j.join(" ")),k.asDestroyStripes=j.slice());var o=[],q,j=this.getElementsByTagName("thead");0!==j.length&&(aa(k.aoHeader,j[0]),o=ma(k));if(null===g.aoColumns){q=[];j=0;for(i=o.length;j").appendTo(this));k.nTHead= +i[0];i=h(this).children("tbody");0===i.length&&(i=h("").appendTo(this));k.nTBody=i[0];i=h(this).children("tfoot");if(0===i.length&&0").appendTo(this);0===i.length||0===i.children().length?h(this).addClass(n.sNoFooter):0a?new q(b[a],this[a]):null},filter:function(a){var b=[];if(y.filter)b=y.filter.call(this,a,this);else for(var c=0,d=this.length;c").addClass(b);h("td",c).addClass(b).html(a)[0].colSpan=Z(d);e.push(c[0])}};if(h.isArray(a)|| +a instanceof h)for(var g=0,j=a.length;g=0?c:f.length+c];var e=typeof a==="string"?a.match(ac):"";if(e)switch(e[2]){case "visIdx":case "visible":a=parseInt(e[1],10);if(a<0){c=h.map(f,function(a, +b){return a.bVisible?b:null});return[c[c.length+a]]}return[ga(b,a)];case "name":return h.map(g,function(a,b){return a===e[1]?b:null})}else return h(j).filter(a).map(function(){return h.inArray(this,j)}).toArray()})});c.selector.cols=a;c.selector.opts=b;return c});v("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh})});v("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf})}); +v("columns().data()","column().data()",function(){return this.iterator("column-rows",function(a,b,c,d,e){for(var c=[],d=0,f=e.length;dd;return!0};p.isDataTable=p.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(p.settings,function(a,e){if(e.nTable===b||e.nScrollHead=== +b||e.nScrollFoot===b)c=!0});return c};p.tables=p.fnTables=function(a){return jQuery.map(p.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable})};p.camelToHungarian=G;r("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var d=h(this.tables().nodes());d[b].apply(d,a);return this})}); +r("clear()",function(){return this.iterator("table",function(a){ja(a)})});r("settings()",function(){return new q(this.context,this.context)});r("data()",function(){return this.iterator("table",function(a){return C(a.aoData,"_aData")}).flatten()});r("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(e),f=h(f),l=h(b.nTableWrapper),m=h.map(b.aoData,function(a){return a.nTr}),o;b.bDestroying= +!0;u(b,"aoDestroyCallback","destroy",[b]);a||(new q(b)).columns().visible(!0);l.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(za).unbind(".DT-"+b.sInstance);e!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&e!=j.parentNode&&(i.children("tfoot").detach(),i.append(j));i.detach();l.detach();b.aaSorting=[];b.aaSortingFixed=[];sa(b);h(m).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&& +(h("th span."+d.sSortIcon+", td span."+d.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+d.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(e,b.nTableReinsertBefore);f.children().detach();f.append(m);i.css("width",b.sDestroyWidth).removeClass(d.sTable);(o=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%o])});c=h.inArray(b,p.settings);-1!==c&&p.settings.splice(c,1)})});p.version="1.10.2";p.settings= +[];p.models={};p.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};p.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};p.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std", +sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};p.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1, +fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null, +fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"}, +sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},p.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",sPaginationType:"simple_numbers", +sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};T(p.defaults);p.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};T(p.defaults.column);p.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null, +bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[], +aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:l,oAjaxData:l, +fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==z(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==z(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a= +this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{}};p.ext=t={classes:{},errMode:"alert",feature:[],search:[],internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:p.fnVersionCheck, +iApiIndex:0,oJUIClasses:{},sVersion:p.version};h.extend(t,{afnFiltering:t.search,aTypes:t.type.detect,ofnSearch:t.type.search,oSort:t.type.order,afnSortData:t.order,aoFeatures:t.feature,oApi:t.internal,oStdClasses:t.classes,oPagination:t.pager});h.extend(p.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter", +sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody", +sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var ya="",ya="",E=ya+"ui-state-default",ea=ya+"css_right ui-icon ui-icon-",Ub=ya+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(p.ext.oJUIClasses,p.ext.classes,{sPageButton:"fg-button ui-button "+E,sPageButtonActive:"ui-state-disabled", +sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:E+" sorting_asc",sSortDesc:E+" sorting_desc",sSortable:E+" sorting",sSortableAsc:E+" sorting_asc_disabled",sSortableDesc:E+" sorting_desc_disabled",sSortableNone:E+" sorting_disabled",sSortJUIAsc:ea+"triangle-1-n",sSortJUIDesc:ea+"triangle-1-s",sSortJUI:ea+"carat-2-n-s",sSortJUIAscAllowed:ea+"carat-1-n",sSortJUIDescAllowed:ea+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper", +sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+E,sScrollFoot:"dataTables_scrollFoot "+E,sHeaderTH:E,sFooterTH:E,sJUIHeader:Ub+" ui-corner-tl ui-corner-tr",sJUIFooter:Ub+" ui-corner-bl ui-corner-br"});var Lb=p.ext.pager;h.extend(Lb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",Ua(a,b),"next"]},full_numbers:function(a,b){return["first","previous",Ua(a,b),"next","last"]},_numbers:Ua, +numbers_length:7});h.extend(!0,p.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i,l,m=0,o=function(b,d){var k,p,r,q,s=function(b){Ra(a,b.data.action,true)};k=0;for(p=d.length;k").appendTo(b);o(r,q)}else{l=i="";switch(q){case "ellipsis":b.append("");break;case "first":i=j.sFirst;l=q+(e>0?"":" "+g.sPageButtonDisabled);break;case "previous":i=j.sPrevious;l=q+(e>0?"":" "+g.sPageButtonDisabled); +break;case "next":i=j.sNext;l=q+(e",{"class":g.sPageButton+" "+l,"aria-controls":a.sTableId,"data-dt-idx":m,tabindex:a.iTabIndex,id:c===0&&typeof q==="string"?a.sTableId+"_"+q:null}).html(i).appendTo(b);Ta(r,{action:q},s);m++}}}};try{var k=h(O.activeElement).data("dt-idx");o(h(b).empty(),d);k!==null&&h(b).find("[data-dt-idx="+k+"]").focus()}catch(p){}}}}); +var va=function(a,b,c,d){if(!a||"-"===a)return-Infinity;b&&(a=Pb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};h.extend(t.type.order,{"date-pre":function(a){return Date.parse(a)||0},"html-pre":function(a){return H(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return H(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return ab?1:0},"string-desc":function(a,b){return a +b?-1:0}});cb("");h.extend(p.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c)?"num"+c:null},function(a){if(a&&(!Yb.test(a)||!Zb.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||H(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Qb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Qb(a,c,!0)?"html-num-fmt"+c:null},function(a){return H(a)||"string"=== +typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(p.ext.type.search,{html:function(a){return H(a)?a:"string"===typeof a?a.replace(Nb," ").replace(wa,""):""},string:function(a){return H(a)?a:"string"===typeof a?a.replace(Nb," "):a}});h.extend(!0,p.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a, +b,c,d){var e=c.idx;h("
").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(f,g,h,i){if(a===g){b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(i[e]=="asc"?d.sSortAsc:i[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(i[e]=="asc"?d.sSortJUIAsc: +i[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});p.render={number:function(a,b,c,d){return{display:function(e){var f=0>e?"-":"",e=Math.abs(parseFloat(e)),g=parseInt(e,10),e=c?b+(e-g).toFixed(c).substring(2):"";return f+(d||"")+g.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+e}}}};h.extend(p.ext.internal,{_fnExternApiFunc:Mb,_fnBuildAjax:na,_fnAjaxUpdate:jb,_fnAjaxParameters:sb,_fnAjaxUpdateDraw:tb,_fnAjaxDataSrc:oa,_fnAddColumn:Aa,_fnColumnOptions:fa,_fnAdjustColumnSizing:V,_fnVisibleToColumnIndex:ga, +_fnColumnIndexToVisible:Y,_fnVisbleColumns:Z,_fnGetColumns:X,_fnColumnTypes:Da,_fnApplyColumnDefs:hb,_fnHungarianMap:T,_fnCamelToHungarian:G,_fnLanguageCompat:N,_fnBrowserDetect:fb,_fnAddData:I,_fnAddTr:ha,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==l?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:A,_fnSetCellData:Ea,_fnSplitObjNotation:Ga,_fnGetObjectDataFn:U,_fnSetObjectDataFn:Ba,_fnGetDataMaster:Ha,_fnClearTable:ja, +_fnDeleteIndex:ka,_fnInvalidateRow:la,_fnGetRowElements:ia,_fnCreateTr:Fa,_fnBuildHead:ib,_fnDrawHead:ba,_fnDraw:K,_fnReDraw:L,_fnAddOptionsHtml:lb,_fnDetectHeader:aa,_fnGetUniqueThs:ma,_fnFeatureHtmlFilter:nb,_fnFilterComplete:ca,_fnFilterCustom:wb,_fnFilterColumn:vb,_fnFilter:ub,_fnFilterCreateSearch:Na,_fnEscapeRegex:Oa,_fnFilterData:xb,_fnFeatureHtmlInfo:qb,_fnUpdateInfo:Ab,_fnInfoMacros:Bb,_fnInitialise:ra,_fnInitComplete:pa,_fnLengthChange:Pa,_fnFeatureHtmlLength:mb,_fnFeatureHtmlPaginate:rb, +_fnPageChange:Ra,_fnFeatureHtmlProcessing:ob,_fnProcessingDisplay:B,_fnFeatureHtmlTable:pb,_fnScrollDraw:W,_fnApplyToChildren:F,_fnCalculateColumnWidths:Ca,_fnThrottle:Ma,_fnConvertToWidth:Cb,_fnScrollingWidthAdjust:Eb,_fnGetWidestNode:Db,_fnGetMaxLenString:Fb,_fnStringToCss:s,_fnScrollBarWidth:Gb,_fnSortFlatten:R,_fnSort:kb,_fnSortAria:Ib,_fnSortListener:Sa,_fnSortAttachListener:Ka,_fnSortingClasses:sa,_fnSortData:Hb,_fnSaveState:ta,_fnLoadState:Jb,_fnSettingsFromNode:ua,_fnLog:P,_fnMap:D,_fnBindAction:Ta, +_fnCallbackReg:x,_fnCallbackFire:u,_fnLengthOverflow:Qa,_fnRenderer:La,_fnDataSource:z,_fnRowAttributes:Ia,_fnCalculateEnd:function(){}});h.fn.dataTable=p;h.fn.dataTableSettings=p.settings;h.fn.dataTableExt=p.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(p,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],N):"object"===typeof exports?N(require("jquery")):jQuery&&!jQuery.fn.dataTable&&N(jQuery)})(window, +document); diff --git a/app/assets/javascripts/firefly/transactions.js b/app/assets/javascripts/firefly/transactions.js index ca7013f11b..3e92ce61c5 100644 --- a/app/assets/javascripts/firefly/transactions.js +++ b/app/assets/javascripts/firefly/transactions.js @@ -1,10 +1,72 @@ -$.getJSON('json/expense-accounts').success(function (data) { - $('input[name="expense_account"]').typeahead({ source: data }); -}); -$.getJSON('json/revenue-accounts').success(function (data) { - $('input[name="revenue_account"]').typeahead({ source: data }); -}); +if ($('input[name="expense_account"]').length > 0) { + $.getJSON('json/expense-accounts').success(function (data) { + $('input[name="expense_account"]').typeahead({source: data}); + }); +} +if ($('input[name="revenue_account"]').length > 0) { + $.getJSON('json/revenue-accounts').success(function (data) { + $('input[name="revenue_account"]').typeahead({source: data}); + }); +} +if ($('input[name="category"]').length > 0) { + $.getJSON('json/categories').success(function (data) { + $('input[name="category"]').typeahead({source: data}); + }); +} -$.getJSON('json/categories').success(function (data) { - $('input[name="category"]').typeahead({ source: data }); +$(document).ready(function () { + $('#transactionTable').DataTable( + { + serverSide: true, + ajax: URL, + paging: true, + processing: true, + order: [], + "lengthMenu": [[50, 100, 250, -1], [50, 100, 250, "All"]], + columns: [ + {name: 'date', data: 'date'}, + { + name: 'description', + data: 'description', + render: function (data, type, full, meta) { + return ' '+ + '' + data.description + ''; + } + }, + { + name: 'amount', + data: 'amount', + render: function (data, type, full, meta) { + return '\u20AC ' + data.toFixed(2) + ''; + } + }, + { + name: 'from', + data: 'from', + render: function (data, type, full, meta) { + return '' + data.name + ''; + } + }, + { + name: 'to', + data: 'to', + render: function (data, type, full, meta) { + return '' + data.name + ''; + } + }, + { + name: 'id', + data: 'id', + render: function (data, type, full, meta) { + return ''; + } + } + ] + } + ); }); \ No newline at end of file diff --git a/app/assets/javascripts/transactions.js b/app/assets/javascripts/transactions.js index 789d6ab377..037dc9d225 100644 --- a/app/assets/javascripts/transactions.js +++ b/app/assets/javascripts/transactions.js @@ -11,4 +11,5 @@ // The available directives right now are require, require_directory, and require_tree // //= require typeahead/bootstrap3-typeahead.min -//= require firefly/transactions +//= require datatables/jquery.dataTables +//= require firefly/transactions \ No newline at end of file diff --git a/app/assets/stylesheets/datatables/jquery.dataTables.min.css b/app/assets/stylesheets/datatables/jquery.dataTables.min.css new file mode 100644 index 0000000000..a2c5489c89 --- /dev/null +++ b/app/assets/stylesheets/datatables/jquery.dataTables.min.css @@ -0,0 +1 @@ +table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting{cursor:pointer;*cursor:hand}table.dataTable thead .sorting{background:url("../images/sort_both.png") no-repeat center right}table.dataTable thead .sorting_asc{background:url("../images/sort_asc.png") no-repeat center right}table.dataTable thead .sorting_desc{background:url("../images/sort_desc.png") no-repeat center right}table.dataTable thead .sorting_asc_disabled{background:url("../images/sort_asc_disabled.png") no-repeat center right}table.dataTable thead .sorting_desc_disabled{background:url("../images/sort_desc_disabled.png") no-repeat center right}table.dataTable tbody tr{background-color:#fff}table.dataTable tbody tr.selected{background-color:#b0bed9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#abb9d3}table.dataTable.hover tbody tr:hover,table.dataTable.hover tbody tr.odd:hover,table.dataTable.hover tbody tr.even:hover,table.dataTable.display tbody tr:hover,table.dataTable.display tbody tr.odd:hover,table.dataTable.display tbody tr.even:hover{background-color:#f5f5f5}table.dataTable.hover tbody tr:hover.selected,table.dataTable.hover tbody tr.odd:hover.selected,table.dataTable.hover tbody tr.even:hover.selected,table.dataTable.display tbody tr:hover.selected,table.dataTable.display tbody tr.odd:hover.selected,table.dataTable.display tbody tr.even:hover.selected{background-color:#a9b7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#f9f9f9}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad4}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:#f5f5f5}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b3cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a7b5ce}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b6d0}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#f9f9f9}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fbfbfb}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fdfdfd}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad4}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#adbbd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.display tbody tr.odd:hover>.sorting_1,table.dataTable.display tbody tr.even:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr.odd:hover>.sorting_1,table.dataTable.order-column.hover tbody tr.even:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.display tbody tr.odd:hover>.sorting_2,table.dataTable.display tbody tr.even:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr.odd:hover>.sorting_2,table.dataTable.order-column.hover tbody tr.even:hover>.sorting_2{background-color:#ebebeb}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.display tbody tr.odd:hover>.sorting_3,table.dataTable.display tbody tr.even:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr.odd:hover>.sorting_3,table.dataTable.order-column.hover tbody tr.even:hover>.sorting_3{background-color:#eee}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.display tbody tr.odd:hover.selected>.sorting_1,table.dataTable.display tbody tr.even:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr.odd:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr.even:hover.selected>.sorting_1{background-color:#a1aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.display tbody tr.odd:hover.selected>.sorting_2,table.dataTable.display tbody tr.even:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr.odd:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr.even:hover.selected>.sorting_2{background-color:#a2afc8}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.display tbody tr.odd:hover.selected>.sorting_3,table.dataTable.display tbody tr.even:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr.odd:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr.even:hover.selected>.sorting_3{background-color:#a4b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:5px 9px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:5px 9px 3px 9px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px 5px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #cacaca;background-color:#fff;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}} diff --git a/app/assets/stylesheets/transactions.css b/app/assets/stylesheets/transactions.css new file mode 100644 index 0000000000..3ef8acf977 --- /dev/null +++ b/app/assets/stylesheets/transactions.css @@ -0,0 +1,13 @@ +/** + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, + * can be referenced here using a relative path. + * + * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it + * gets included (e.g. say you have require_tree . then the code will appear after all the directories + * but before any files alphabetically greater than 'application.css' + * + *= require datatables/jquery.dataTables.min + */ \ No newline at end of file diff --git a/app/controllers/JsonController.php b/app/controllers/JsonController.php index bb753bd143..07dd7773c9 100644 --- a/app/controllers/JsonController.php +++ b/app/controllers/JsonController.php @@ -4,6 +4,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI; use Firefly\Storage\Budget\BudgetRepositoryInterface as Bud; use Firefly\Storage\Category\CategoryRepositoryInterface as Cat; use Firefly\Storage\Component\ComponentRepositoryInterface as CRI; +use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI; +use Illuminate\Support\Collection; /** * Class JsonController @@ -16,19 +18,159 @@ class JsonController extends BaseController protected $_components; protected $_categories; protected $_budgets; + /** @var TJRI $_journals */ + protected $_journals; /** - * @param ARI $accounts - * @param CRI $components - * @param Cat $categories - * @param Bud $budgets + * @param ARI $accounts + * @param CRI $components + * @param Cat $categories + * @param Bud $budgets + * @param TJRI $journals */ - public function __construct(ARI $accounts, CRI $components, Cat $categories, Bud $budgets) + public function __construct(ARI $accounts, CRI $components, Cat $categories, Bud $budgets, TJRI $journals) { $this->_components = $components; $this->_accounts = $accounts; $this->_categories = $categories; $this->_budgets = $budgets; + $this->_journals = $journals; + } + + /** + * Returns a list of transactions, expenses only, using the given parameters. + */ + public function expenses() + { + + + /* + * Process all parameters! + */ + $parameters = [ + 'start' => intval(Input::get('start')), + 'length' => intval(Input::get('length')), + 'draw' => intval(Input::get('draw')), + ]; + + + /* + * Columns: + */ + if (!is_null(Input::get('columns')) && is_array(Input::get('columns'))) { + foreach (Input::get('columns') as $column) { + $parameters['columns'][] = [ + 'data' => $column['data'], + 'name' => $column['name'], + 'searchable' => $column['searchable'] == 'true' ? true : false, + 'orderable' => $column['orderable'] == 'true' ? true : false, + 'search' => [ + 'value' => $column['search']['value'], + 'regex' => $column['search']['regex'] == 'true' ? true : false, + ] + ]; + } + } + + + /* + * Sorting. + */ + if (!is_null(Input::get('order')) && is_array(Input::get('order'))) { + foreach (Input::get('order') as $order) { + $columnIndex = intval($order['column']); + $columnName = $parameters['columns'][$columnIndex]['name']; + $parameters['order'][] = [ + 'name' => $columnName, + 'dir' => strtoupper($order['dir']) + ]; + } + } + /* + * Search parameters: + */ + if (!is_null(Input::get('search')) && is_array(Input::get('search'))) { + $search = Input::get('search'); + $parameters['search'] = [ + 'value' => $search['value'], + 'regex' => $search['regex'] == 'true' ? true : false + ]; + } + + /* + * Build query: + */ + $query = \TransactionJournal::lessThan(0)->transactionTypes(['Withdrawal'])->withRelevantData(); + $count = $query->count(); + $query->take($parameters['length']); + $query->skip($parameters['start']); + + /* + * Add sort parameters to query: + */ + \Debugbar::startMeasure('order'); + if (isset($parameters['order']) && count($parameters['order']) > 0) { + foreach ($parameters['order'] as $order) { + $query->orderBy($order['name'], $order['dir']); + } + } else { + $query->defaultSorting(); + } + + /* + * Build return array: + */ + $data = [ + 'draw' => $parameters['draw'], + 'recordsTotal' => $count, + 'recordsFiltered' => $count, + 'data' => [], + + ]; + + /* + * Get paginated result set: + */ + /** @var Collection $set */ + $set = $query->get(['transaction_journals.*', 'transactions.amount']); + + /* + * Loop set and create entries to return. + */ + foreach ($set as $entry) { + $from = $entry->transactions[0]->account; + $to = $entry->transactions[1]->account; + $data['data'][] = [ + 'date' => $entry->date->format('j F Y'), + 'description' => [ + 'description' => $entry->description, + 'url' => route('transactions.show', $entry->id) + ], + 'amount' => floatval($entry->amount), + 'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)], + 'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)], + 'id' => [ + 'edit' => route('transactions.edit', $entry->id), + 'delete' => route('transactions.delete', $entry->id) + ] + ]; + } + + /* + * Build return data: + */ + $return = $data; + + if (Input::get('debug') == 'true') { + echo '
';
+            print_r($parameters);
+            echo '
'; + print_r($return); + Response::json($return); + + } else { + return Response::json($return); + } } /** @@ -36,7 +178,7 @@ class JsonController extends BaseController */ public function expenseAccounts() { - $list = $this->_accounts->getOfTypes(['Expense account','Beneficiary account']); + $list = $this->_accounts->getOfTypes(['Expense account', 'Beneficiary account']); $return = []; foreach ($list as $entry) { $return[] = $entry->name; diff --git a/app/controllers/TransactionController.php b/app/controllers/TransactionController.php index 0c668c23df..9dcc0fc93e 100644 --- a/app/controllers/TransactionController.php +++ b/app/controllers/TransactionController.php @@ -16,11 +16,20 @@ class TransactionController extends BaseController { protected $_repository; + protected $_helper; + /** @var Carbon|null $_start */ + protected $_start; + + /** @var Carbon|null $_end */ + protected $_end; + /** + * Construct a new transaction controller with two of the most often used helpers. + * * @param TJRI $repository - * @param TI $helper + * @param TI $helper */ public function __construct(TJRI $repository, TI $helper) { @@ -28,35 +37,49 @@ class TransactionController extends BaseController $this->_helper = $helper; View::share('title', 'Transactions'); View::share('mainTitleIcon', 'fa-repeat'); + + /* + * With this construction, every method has access to a possibly set start + * and end date, to be used at their leisure: + */ + $this->_start = is_null(Input::get('startdate')) ? null : new Carbon(Input::get('startdate')); + $this->_end = is_null(Input::get('enddate')) ? null : new Carbon(Input::get('enddate')); } /** + * Shows the view helping the user to create a new transaction journal. + * * @param string $what * * @return \Illuminate\View\View */ public function create($what = 'deposit') { + /* + * The repositories we need: + */ /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - // get asset accounts with names and id's. /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $list = $accountRepository->getActiveDefault(); - $assetAccounts = $toolkit->makeSelectList($list); - - // get budgets as a select list. /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); - $budgets = $toolkit->makeSelectList($budgetRepository->get()); - $budgets[0] = '(no budget)'; - // get the piggy banks. /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); - $piggies = $piggyRepository->get(); + + // get asset accounts with names and id's. + $assetAccounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); + + // get budgets as a select list. + $budgets = $toolkit->makeSelectList($budgetRepository->get()); + $budgets[0] = '(no budget)'; + + // get the piggy banks. + $piggies = $toolkit->makeSelectList($piggyRepository->get()); + $piggies[0] = '(no piggy bank)'; return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with( 'what', $what @@ -64,19 +87,19 @@ class TransactionController extends BaseController } /** + * Shows the form that allows a user to delete a transaction journal. + * * @param TransactionJournal $transactionJournal * * @return $this */ public function delete(TransactionJournal $transactionJournal) { - View::share( - 'subTitle', - 'Delete ' . strtolower($transactionJournal->transactionType->type) . ' "' . $transactionJournal->description - . '"' - ); + $type = strtolower($transactionJournal->transactionType->type); - return View::make('transactions.delete')->with('journal', $transactionJournal); + return View::make('transactions.delete')->with('journal', $transactionJournal)->with( + 'subTitle', 'Delete ' . $type . ' "' . $transactionJournal->description . '"' + ); } @@ -89,65 +112,89 @@ class TransactionController extends BaseController */ public function destroy(TransactionJournal $transactionJournal) { + $type = $transactionJournal->transactionType->type; $transactionJournal->delete(); - return Redirect::route('transactions.index'); - + switch ($type) { + case 'Withdrawal': + return Redirect::route('transactions.expenses'); + break; + case 'Deposit': + return Redirect::route('transactions.revenue'); + break; + case 'Transfer': + return Redirect::route('transactions.transfers'); + break; + } } /** + * Shows the view to edit a transaction. + * * @param TransactionJournal $journal * * @return $this */ public function edit(TransactionJournal $journal) { - // type is useful for display: - $what = strtolower($journal->transactiontype->type); - - // some lists prefilled: - // get accounts with names and id's. + /* + * All the repositories we need: + */ /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - // get asset accounts with names and id's. /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $list = $accountRepository->getActiveDefault(); - $accounts = $toolkit->makeSelectList($list); - View::share( - 'subTitle', 'Edit ' . strtolower($journal->transactionType->type) . ' "' . $journal->description . '"' - ); - - // get budgets as a select list. /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); - $budgets = $budgetRepository->getAsSelectList(); - $budgets[0] = '(no budget)'; - // get the piggy banks. /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); - $piggies = $piggyRepository->get(); - // piggy bank id? + + // type is useful for display: + $what = strtolower($journal->transactiontype->type); + + // get asset accounts with names and id's. + $accounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); + + // get budgets as a select list. + $budgets = $budgetRepository->getAsSelectList(); + $budgets[0] = '(no budget)'; + + /* + * Get all piggy banks plus (if any) the relevant piggy bank. Since just one + * of the transactions in the journal has this field, it should all fill in nicely. + */ + $piggies = $piggyRepository->get(); $piggyBankId = null; foreach ($journal->transactions as $t) { $piggyBankId = $t->piggybank_id; } - // data to properly display form: - $data = [ + /* + * Data to properly display the edit form. + */ + $data = [ 'date' => $journal->date->format('Y-m-d'), 'category' => '', 'budget_id' => 0, 'piggybank_id' => $piggyBankId ]; + + /* + * Fill in the category. + */ $category = $journal->categories()->first(); if (!is_null($category)) { $data['category'] = $category->name; } - switch ($journal->transactiontype->type) { + + /* + * Switch on the type of transaction edited by the user and fill in other + * relevant fields: + */ + switch ($what) { case 'Withdrawal': $data['account_id'] = $journal->transactions[0]->account->id; $data['beneficiary'] = $journal->transactions[1]->account->name; @@ -169,30 +216,27 @@ class TransactionController extends BaseController break; } + /* + * Show the view. + */ return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with( 'what', $what - )->with('budgets', $budgets)->with('data', $data)->with('piggies', $piggies); + )->with('budgets', $budgets)->with('data', $data)->with('piggies', $piggies)->with( + 'subTitle', 'Edit ' . $what . ' "' . $journal->description . '"' + ); } + /** + * @return $this + */ public function expenses() { - $transactionType = $this->_repository->getTransactionType('Withdrawal'); - $start = is_null(Input::get('startdate')) ? null : new Carbon(Input::get('startdate')); - $end = is_null(Input::get('enddate')) ? null : new Carbon(Input::get('enddate')); - if ($start <= $end && !is_null($start) && !is_null($end)) { - $journals = $this->_repository->paginate($transactionType, 25, $start, $end); - $filtered = true; - $filters = ['start' => $start, 'end' => $end]; - } else { - $journals = $this->_repository->paginate($transactionType, 25); - $filtered = false; - $filters = null; - } + #$transactionType = $this->_repository->getTransactionType('Withdrawal'); + #$journals = $this->_repository->paginate($transactionType, 25, $this->_start, $this->_end); - View::share('subTitleIcon', 'fa-long-arrow-left'); - return View::make('transactions.index')->with('journals', $journals)->with('filtered', $filtered)->with( - 'filters', $filters - )->with('subTitle', 'Expenses'); + return View::make('transactions.list')->with('subTitle', 'Expenses')->with( + 'subTitleIcon', 'fa-long-arrow-left' + ); } public function revenue() @@ -298,7 +342,7 @@ class TransactionController extends BaseController /* * Failure! */ - if($messageBag->count() > 0) { + if ($messageBag->count() > 0) { Session::flash('error', 'Could not save transaction: ' . $messageBag->first()); return Redirect::route('transactions.create', [$what])->withInput()->withErrors($messageBag); } diff --git a/app/lib/Firefly/Helper/Controllers/Transaction.php b/app/lib/Firefly/Helper/Controllers/Transaction.php index e1b451e8a5..36e42cca2d 100644 --- a/app/lib/Firefly/Helper/Controllers/Transaction.php +++ b/app/lib/Firefly/Helper/Controllers/Transaction.php @@ -11,23 +11,61 @@ use Illuminate\Support\MessageBag; */ class Transaction implements TransactionInterface { + protected $_user = null; + + /** + * + */ + public function __construct() + { + $this->_user = \Auth::user(); + } + + /** + * @param \User $user + * + * @return mixed|void + */ + public function overruleUser(\User $user) + { + $this->_user = $user; + return true; + } /** * Store a full transaction journal and associated stuff * * @param array $data * - * @return MessageBag + * @return MessageBag|\TransactionJournal * * @SuppressWarnings(PHPMD.ShortVariable) */ public function store(array $data) { /* - * save journal using repository + * All the repositories we need: */ /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); + $journals->overruleUser($this->_user); + + /** @var \Firefly\Storage\Category\CategoryRepositoryInterface $categories */ + $categories = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface'); + $categories->overruleUser($this->_user); + + /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */ + $budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); + $budgets->overruleUser($this->_user); + + /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggybanks */ + $piggybanks = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); + $piggybanks->overruleUser($this->_user); + + + /* + * save journal using repository + */ $journal = $journals->store($data); /* @@ -41,18 +79,22 @@ class Transaction implements TransactionInterface * save budget using repository */ if (isset($data['budget_id'])) { - /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */ - $budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); $budget = $budgets->find($data['budget_id']); } /* * save category using repository */ - /** @var \Firefly\Storage\Category\CategoryRepositoryInterface $categories */ - $categories = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface'); $category = $categories->firstOrCreate($data['category']); + /* + * Find piggy bank using repository: + */ + $piggybank = null; + if(isset($data['piggybank_id'])) { + $piggybank = $piggybanks->find($data['piggybank_id']); + } + /* * save accounts using repositories * this depends on the kind of transaction and i've yet to fix this. @@ -76,6 +118,8 @@ class Transaction implements TransactionInterface if (isset($data['account_to_id'])) { $to = $accounts->findAssetAccountById($data['account_to_id']); } + + /* * Add a custom error when they are the same. */ @@ -86,10 +130,14 @@ class Transaction implements TransactionInterface } /* - * save transactions using repository. + * Save transactions using repository. We try to connect the (possibly existing) + * piggy bank to either transaction, knowing it will only work with one of them. */ + /** @var \Transaction $one */ $one = $journals->saveTransaction($journal, $from, floatval($data['amount']) * -1); + $one->connectPiggybank($piggybank); $two = $journals->saveTransaction($journal, $to, floatval($data['amount'])); + $two->connectPiggybank($piggybank); /* * Count for $journal is zero? Then there were errors! */ @@ -103,7 +151,7 @@ class Transaction implements TransactionInterface } /* - * Connect budget and category: + * Connect budget, category and piggy bank: */ if (isset($budget) && !is_null($budget)) { $journal->budgets()->save($budget); @@ -111,8 +159,16 @@ class Transaction implements TransactionInterface if (!is_null($category)) { $journal->categories()->save($category); } + if(isset($piggybank) && !is_null($piggybank)) { + // some trigger? + + //$journal->piggybanks()->save($piggybank); + } $journal->completed = true; $journal->save(); + if(isset($data['return_journal']) && $data['return_journal'] == true) { + return $journal; + } return $journal->errors(); } diff --git a/app/lib/Firefly/Helper/Controllers/TransactionInterface.php b/app/lib/Firefly/Helper/Controllers/TransactionInterface.php index 50529788cb..7ecafcd3a5 100644 --- a/app/lib/Firefly/Helper/Controllers/TransactionInterface.php +++ b/app/lib/Firefly/Helper/Controllers/TransactionInterface.php @@ -19,4 +19,13 @@ interface TransactionInterface { */ public function store(array $data); + /** + * Overrule the user used when the class is created. + * + * @param \User $user + * + * @return mixed + */ + public function overruleUser(\User $user); + } \ No newline at end of file diff --git a/app/lib/Firefly/Queue/Import.php b/app/lib/Firefly/Queue/Import.php index 7136e53843..dfa26e3391 100644 --- a/app/lib/Firefly/Queue/Import.php +++ b/app/lib/Firefly/Queue/Import.php @@ -336,7 +336,7 @@ class Import $accountID = intval($payload['data']['value']); /* - * Is account imported already. + * Is account imported already? */ $importEntry = $this->_repository->findImportEntry($importMap, 'Account', $accountID); @@ -349,11 +349,13 @@ class Import /* * Update all piggy banks. */ - \Log::debug('Updating all piggybanks, found the right setting.'); - foreach ($all as $piggy) { - $piggy->account()->associate($account); - unset($piggy->leftInAccount); - $piggy->save(); + if (!is_null($account)) { + \Log::debug('Updating all piggybanks, found the right setting.'); + foreach ($all as $piggy) { + $piggy->account()->associate($account); + unset($piggy->leftInAccount); + $piggy->save(); + } } } else { \Log::notice('Account not yet imported, hold or 5 minutes.'); @@ -587,7 +589,7 @@ class Import \Queue::push('Firefly\Queue\Import@cleanImportAccount', ['mapID' => $importMap->id]); $job->delete(); // count fixed - + \Log::debug('Done with job "start"'); } } diff --git a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php index dd2c64e3ea..221cd8b877 100644 --- a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php +++ b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php @@ -95,6 +95,17 @@ class EloquentAccountRepository implements AccountRepositoryInterface $type = $this->findAccountType('Expense account'); $account = $this->_user->accounts()->where('name', $name)->where('account_type_id', $type->id)->first(); + // create if not found: + if(strlen($name) > 0) { + $set = [ + 'name' => $name, + 'user_id' => $this->_user->id, + 'active' => 1, + 'account_type_id' => $type->id + ]; + $account = $this->firstOrCreate($set); + } + // find cash account as fall back: if (is_null($account)) { $cashType = $this->findAccountType('Cash account'); @@ -156,7 +167,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface /** * @param $type * - * @return mixed + * @return \AccountType|null */ public function findAccountType($type) { @@ -498,10 +509,22 @@ class EloquentAccountRepository implements AccountRepositoryInterface */ protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date) { - // get account type: - $initialBalanceAT = \AccountType::where('type', 'Initial balance account')->first(); + /* + * The repositories we need: + */ + /** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */ + $transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface'); + $transactions->overruleUser($this->_user); - // create new account: + + /* + * get account type: + */ + $initialBalanceAT = $this->findAccountType('Initial balance account'); + + /* + * create new account + */ $initial = new \Account; $initial->accountType()->associate($initialBalanceAT); $initial->user()->associate($this->_user); @@ -509,16 +532,20 @@ class EloquentAccountRepository implements AccountRepositoryInterface $initial->active = 0; if ($initial->validate()) { $initial->save(); - // create new transaction journal (and transactions): - /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */ - $transactionJournal = \App::make( - 'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface' - ); - $transactionJournal->overruleUser($this->_user); + /* + * create new transaction journal (and transactions): + */ - $transactionJournal->createSimpleJournal( - $initial, $account, 'Initial Balance for ' . $account->name, $amount, $date - ); + $set =[ + 'account_from_id' => $initial->id, + 'account_to_id' => $account->id, + 'description' => 'Initial Balance for ' . $account->name, + 'what' => 'Opening balance', + 'amount' => $amount, + 'category' => '', + 'date' => $date->format('Y-m-d') + ]; + $transactions->store($set); return true; } diff --git a/app/lib/Firefly/Storage/Piggybank/EloquentPiggybankRepository.php b/app/lib/Firefly/Storage/Piggybank/EloquentPiggybankRepository.php index 5e4299b4a0..1965f70d49 100644 --- a/app/lib/Firefly/Storage/Piggybank/EloquentPiggybankRepository.php +++ b/app/lib/Firefly/Storage/Piggybank/EloquentPiggybankRepository.php @@ -128,7 +128,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface /* * Already have a piggy bank with this name, we skip it. */ - $this->_repository->store($importMap, 'Piggybank', $payload['data']['id'], $piggyBank->id); + $repository->store($importMap, 'Piggybank', $payload['data']['id'], $piggyBank->id); \Log::debug('Already imported piggy "' . $payload['data']['name'] . '".'); } // update map: diff --git a/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php b/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php index d076b8936d..53f700bb13 100644 --- a/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php +++ b/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php @@ -56,6 +56,10 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); $accounts->overruleUser($user); + /** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */ + $transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface'); + $transactions->overruleUser($this->_user); + /* * Prep some variables from the payload: */ @@ -114,9 +118,18 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito /* * Import transfer: */ + $set = [ + 'account_from_id' => $accountFrom->id, + 'account_to_id' => $accountTo->id, + 'amount' => $amount, + 'description' => $description, + 'date' => $date->format('Y-m-d'), + 'category' => '', + 'what' => 'transfer', + 'return_journal' => true + ]; + $journal = $transactions->store($set); - - $journal = $this->createSimpleJournal($accountFrom, $accountTo, $description, $amount, $date); $repository->store($importMap, 'Transfer', $transferId, $journal->id); \Log::debug('Imported transfer "' . $description . '" (' . $amount . ') (' . $date->format('Y-m-d') . ')'); @@ -175,7 +188,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito * @param \Account $account * @param $amount * - * @return mixed + * @return \Transaction|null */ public function saveTransaction(\TransactionJournal $journal, \Account $account, $amount) { @@ -217,11 +230,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito return; } - /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */ $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); $accounts->overruleUser($user); + /** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */ + $transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface'); + $transactions->overruleUser($this->_user); + + /* * Prep some vars coming out of the pay load: */ @@ -249,7 +266,6 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito return; } - /* * Find or create the "import account" which is used because at this point, Firefly * doesn't know which beneficiary (expense account) should be connected to this transaction. @@ -286,6 +302,16 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito } return; } + /* + * Prep some data for the import routine: + */ + $set = [ + 'category' => '', + 'description' => $description, + 'date' => $date->format('Y-m-d'), + 'return_journal' => true + ]; + /* * If the amount is less than zero, we move money to the $importAccount. Otherwise, @@ -293,22 +319,27 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito */ if ($amount < 0) { // if amount is less than zero, move to $importAccount - $accountFrom = $assetAccount; - $accountTo = $importAccount; + $accountFrom = $assetAccount; + $accountTo = $importAccount; + $set['what'] = 'withdrawal'; + $set['account_id'] = $accountFrom->id; + $set['expense_account'] = $importAccount->name; } else { - $accountFrom = $importAccount; - $accountTo = $assetAccount; + $accountFrom = $importAccount; + $accountTo = $assetAccount; + $set['what'] = 'deposit'; + $set['account_id'] = $accountTo->id; + $set['revenue_account'] = $accountFrom->name; } /* * Modify the amount so it will work with or new transaction journal structure. */ - $amount = $amount < 0 ? $amount * -1 : $amount; - + $set['amount'] = $amount < 0 ? $amount * -1 : $amount; /* * Import it: */ - $journal = $this->createSimpleJournal($accountFrom, $accountTo, $description, $amount, $date); + $journal = $transactions->store($set); $repository->store($importMap, 'Transaction', $transactionId, $journal->id); \Log::debug('Imported transaction "' . $description . '" (' . $amount . ') (' . $date->format('Y-m-d') . ')'); @@ -337,14 +368,6 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito ->where('id', $journalId)->first(); } -// /** -// * -// */ -// public function get() -// { -// -// } - /** * @param $type * diff --git a/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php b/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php index 94e14a8091..a5c703bfa9 100644 --- a/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php +++ b/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php @@ -74,7 +74,7 @@ interface TransactionJournalRepositoryInterface * @param \TransactionJournal $journal * @param $data * - * @return mixed + * @return \Transaction|null */ public function update(\TransactionJournal $journal, $data); diff --git a/app/models/Transaction.php b/app/models/Transaction.php index 9452d0f421..0c1a2d6531 100644 --- a/app/models/Transaction.php +++ b/app/models/Transaction.php @@ -8,20 +8,20 @@ use LaravelBook\Ardent\Builder; /** * Transaction * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $account_id - * @property integer $piggybank_id - * @property integer $transaction_journal_id - * @property string $description - * @property float $amount - * @property-read \Account $account - * @property-read \Illuminate\Database\Eloquent\Collection|\Budget[] $budgets - * @property-read \Illuminate\Database\Eloquent\Collection|\Category[] $categories + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property integer $account_id + * @property integer $piggybank_id + * @property integer $transaction_journal_id + * @property string $description + * @property float $amount + * @property-read \Account $account + * @property-read \Illuminate\Database\Eloquent\Collection|\Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\Category[] $categories * @property-read \Illuminate\Database\Eloquent\Collection|\Component[] $components - * @property-read \Piggybank $piggybank - * @property-read \TransactionJournal $transactionJournal + * @property-read \Piggybank $piggybank + * @property-read \TransactionJournal $transactionJournal * @method static \Illuminate\Database\Query\Builder|\Transaction whereId($value) * @method static \Illuminate\Database\Query\Builder|\Transaction whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\Transaction whereUpdatedAt($value) @@ -57,6 +57,35 @@ class Transaction extends Ardent return $this->belongsTo('Account'); } + /** + * @param Piggybank $piggybank + * + * @return bool + */ + public function connectPiggybank(\Piggybank $piggybank = null) + { + if (is_null($piggybank)) { + return true; + } + /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ + $piggyRepository = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); + if ($this->account_id == $piggybank->account_id) { + $this->piggybank()->associate($piggybank); + $this->save(); + \Event::fire('piggybanks.createRelatedTransfer', [$piggybank, $this->transactionJournal, $this]); + return true; + } + return false; + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function piggybank() + { + return $this->belongsTo('Piggybank'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ @@ -81,14 +110,6 @@ class Transaction extends Ardent return $this->belongsToMany('Component'); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function piggybank() - { - return $this->belongsTo('Piggybank'); - } - public function scopeAccountIs(Builder $query, Account $account) { $query->where('transactions.account_id', $account->id); @@ -97,8 +118,10 @@ class Transaction extends Ardent public function scopeAfter(Builder $query, Carbon $date) { if (is_null($this->joinedJournals)) { - $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id'); + $query->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', + 'transactions.transaction_journal_id' + ); $this->joinedJournals = true; } $query->where('transaction_journals.date', '>=', $date->format('Y-m-d')); @@ -107,8 +130,10 @@ class Transaction extends Ardent public function scopeBefore(Builder $query, Carbon $date) { if (is_null($this->joinedJournals)) { - $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id'); + $query->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', + 'transactions.transaction_journal_id' + ); $this->joinedJournals = true; } $query->where('transaction_journals.date', '<=', $date->format('Y-m-d')); @@ -127,13 +152,17 @@ class Transaction extends Ardent public function scopeTransactionTypes(Builder $query, array $types) { if (is_null($this->joinedJournals)) { - $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id'); + $query->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', + 'transactions.transaction_journal_id' + ); $this->joinedJournals = true; } if (is_null($this->joinedTransactionTypes)) { - $query->leftJoin('transaction_types', 'transaction_types.id', '=', - 'transaction_journals.transaction_type_id'); + $query->leftJoin( + 'transaction_types', 'transaction_types.id', '=', + 'transaction_journals.transaction_type_id' + ); $this->joinedTransactionTypes = true; } $query->whereIn('transaction_types.type', $types); diff --git a/app/models/TransactionJournal.php b/app/models/TransactionJournal.php index 99cef90950..1f0c35849c 100644 --- a/app/models/TransactionJournal.php +++ b/app/models/TransactionJournal.php @@ -163,6 +163,42 @@ use LaravelBook\Ardent\Builder; * 'Budget[] $budgets * @property-read \Illuminate\Database\Eloquent\Collection|\ * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\ + * 'Category[] $categories */ class TransactionJournal extends Ardent { diff --git a/app/routes.php b/app/routes.php index 10fcde4221..0fb589f0d4 100644 --- a/app/routes.php +++ b/app/routes.php @@ -181,6 +181,7 @@ Route::group(['before' => 'auth'], function () { Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense']); Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue']); Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']); + Route::get('/json/expenses', ['uses' => 'JsonController@expenses', 'as' => 'json.expenses']); // limit controller: Route::get('/budgets/limits/create/{budget?}',['uses' => 'LimitController@create','as' => 'budgets.limits.create']); diff --git a/app/views/lists/transactions.blade.php b/app/views/lists/transactions.blade.php index 8a21ec7fb2..62bf705192 100644 --- a/app/views/lists/transactions.blade.php +++ b/app/views/lists/transactions.blade.php @@ -1,83 +1,98 @@ - +
+ - + - + + @foreach($journals as $journal) - id) - class="success" - @endif - > - id) + class="success" @endif - @if($journal->transactiontype->type == 'Deposit') - - @endif - @if($journal->transactiontype->type == 'Transfer') - - @endif - @if($journal->transactiontype->type == 'Opening balance') - - @endif - - - - - + + + + + @endif + + + + + + @else + + @endif @endforeach @if(isset($sum) && $sum == true) diff --git a/app/views/transactions/create.blade.php b/app/views/transactions/create.blade.php index 4bf784fe71..33605b70b7 100644 --- a/app/views/transactions/create.blade.php +++ b/app/views/transactions/create.blade.php @@ -52,7 +52,9 @@
- + @if($errors->has('expense_account'))

{{$errors->first('expense_account')}}

@else @@ -72,12 +74,16 @@ Revenue account
- + @if($errors->has('beneficiary'))

{{$errors->first('revenue_account')}}

@else - This field will auto-complete your existing revenue accounts (if any), but you can type freely to create new ones. + This field will auto-complete your existing revenue + accounts (where you spent the receive money from), + but you can type freely to create new ones. @endif
@@ -120,7 +126,10 @@ @endif
- + @if($errors->has('amount'))

{{$errors->first('amount')}}

@endif @@ -131,7 +140,8 @@
- + @if($errors->has('date'))

{{$errors->first('date')}}

@endif @@ -176,8 +186,9 @@ @if($errors->has('category'))

{{$errors->first('category')}}

@else - Add more fine-grained information to this transaction by entering a category. - This field will auto-complete existing categories but can also be used to create new ones. + Add more fine-grained information to + this transaction by entering a category. This field will auto-complete + existing categories but can also be used to create new ones. @endif
@@ -192,16 +203,7 @@ Piggy bank
- + {{Form::select('piggybank_id',$budgets,Input::old('piggybank_id') ?: 0,['class' => 'form-control'])}} @if($errors->has('piggybank_id'))

{{$errors->first('piggybank_id')}}

@else @@ -271,7 +273,5 @@ @stop @section('scripts') - - @stop \ No newline at end of file diff --git a/app/views/transactions/delete.blade.php b/app/views/transactions/delete.blade.php index 810d983c47..5bda69d486 100644 --- a/app/views/transactions/delete.blade.php +++ b/app/views/transactions/delete.blade.php @@ -1,44 +1,42 @@ @extends('layouts.default') @section('content') -
-
-

Remember that deleting something is permanent.

- -
- -
- {{Form::open(['class' => 'form-horizontal','url' => route('transactions.destroy',$journal->id)])}}
-
-

- This form allows you to delete the transaction labelled "{{{$journal->description}}}". -

-

- Are you sure? -

- -
-
- - @if($journal->transactiontype->type == 'Withdrawal') - Cancel - @endif - @if($journal->transactiontype->type == 'Deposit') - Cancel - @endif - @if($journal->transactiontype->type == 'Transfer') - Cancel - @endif - +
+ +
+
+ Destroy "{{{$journal->description}}}" +
+
+

+ Deleting stuff from Firefly is permanent. This action will remove the transaction and all + associated data. +

+

+ This action will not destroy categories, piggybanks, accounts, etc. +

+

+ Are you sure? +

+
+ + @if($journal->transactiontype->type == 'Withdrawal') + Cancel + @endif + @if($journal->transactiontype->type == 'Deposit') + Cancel + @endif + @if($journal->transactiontype->type == 'Transfer') + Cancel + @endif +
-
- {{Form::close()}} @stop diff --git a/app/views/transactions/index.blade.php b/app/views/transactions/index.blade.php deleted file mode 100644 index a750f88740..0000000000 --- a/app/views/transactions/index.blade.php +++ /dev/null @@ -1,20 +0,0 @@ -@extends('layouts.default') -@section('content') - - -@if($filtered === true) -

- This view is filtered to show only the transactions between - {{$filters['start']->format('M jS, Y')}} and {{$filters['end']->format('M jS, Y')}}. -

-

- Reset the filter. -

-@endif - - -@include('paginated.transactions') - - -@stop - diff --git a/app/views/transactions/list.blade.php b/app/views/transactions/list.blade.php new file mode 100644 index 0000000000..cbee1762fe --- /dev/null +++ b/app/views/transactions/list.blade.php @@ -0,0 +1,37 @@ +@extends('layouts.default') +@section('content') +
+
+
+
+ {{{$subTitle}}} +
+
+
A Date Description Amount (€) From ToB
- @if($journal->transactiontype->type == 'Withdrawal') - + @if(isset($journal->transactions[0]) && isset($journal->transactions[1])) +
- @foreach($journal->components as $component) - @if($component->class == 'Budget') - - @endif - @if($component->class == 'Category') - - @endif - @endforeach - - @if(!is_null($journal->recurringTransaction)) - - @endif - - {{$journal->date->format('d F Y')}} - {{{$journal->description}}} - @if($journal->transactiontype->type == 'Withdrawal') - {{mf($journal->transactions[1]->amount,false)}} - transactions[1]->amount;?> - @endif - @if($journal->transactiontype->type == 'Deposit') - {{mf($journal->transactions[1]->amount,false)}} - @endif - @if($journal->transactiontype->type == 'Transfer') - {{mf($journal->transactions[1]->amount,false)}} + > + + @if($journal->transactiontype->type == 'Withdrawal') + + @endif + @if($journal->transactiontype->type == 'Deposit') + + @endif + @if($journal->transactiontype->type == 'Transfer') + + @endif + @if($journal->transactiontype->type == 'Opening balance') + + @endif + + @foreach($journal->components as $component) + @if($component->class == 'Budget') + + @endif + @if($component->class == 'Category') + + @endif + @endforeach + + @if(!is_null($journal->recurringTransaction)) + + @endif + + {{$journal->date->format('d F Y')}} + {{{$journal->description}}} + @if($journal->transactiontype->type == 'Withdrawal') + {{mf($journal->transactions[1]->amount,false)}} + transactions[1]->amount;?> + @endif + @if($journal->transactiontype->type == 'Deposit') + {{mf($journal->transactions[1]->amount,false)}} + @endif + @if($journal->transactiontype->type == 'Transfer') + {{mf($journal->transactions[1]->amount,false)}} - @endif - + {{{$journal->transactions[0]->account->name}}} + + {{{$journal->transactions[1]->account->name}}} + + @if($journal->transactiontype->type != 'Opening balance') + + @endif +
+ + + + + + + + + + +
DateDescriptionAmount (€)FromToID
+
+
+
+
+ + +@stop +@section('scripts') + + +@stop +@section('styles') + +@stop \ No newline at end of file diff --git a/public/assets/images/Sorting icons.psd b/public/assets/images/Sorting icons.psd new file mode 100644 index 0000000000..53b2e06850 Binary files /dev/null and b/public/assets/images/Sorting icons.psd differ diff --git a/public/assets/images/back_disabled.png b/public/assets/images/back_disabled.png new file mode 100644 index 0000000000..881de7976f Binary files /dev/null and b/public/assets/images/back_disabled.png differ diff --git a/public/assets/images/back_enabled.png b/public/assets/images/back_enabled.png new file mode 100644 index 0000000000..c608682b04 Binary files /dev/null and b/public/assets/images/back_enabled.png differ diff --git a/public/assets/images/back_enabled_hover.png b/public/assets/images/back_enabled_hover.png new file mode 100644 index 0000000000..d300f1064b Binary files /dev/null and b/public/assets/images/back_enabled_hover.png differ diff --git a/public/assets/images/favicon.ico b/public/assets/images/favicon.ico new file mode 100644 index 0000000000..6eeaa2a0d3 Binary files /dev/null and b/public/assets/images/favicon.ico differ diff --git a/public/assets/images/forward_disabled.png b/public/assets/images/forward_disabled.png new file mode 100644 index 0000000000..6a6ded7de8 Binary files /dev/null and b/public/assets/images/forward_disabled.png differ diff --git a/public/assets/images/forward_enabled.png b/public/assets/images/forward_enabled.png new file mode 100644 index 0000000000..a4e6b5384b Binary files /dev/null and b/public/assets/images/forward_enabled.png differ diff --git a/public/assets/images/forward_enabled_hover.png b/public/assets/images/forward_enabled_hover.png new file mode 100644 index 0000000000..fc46c5ebf0 Binary files /dev/null and b/public/assets/images/forward_enabled_hover.png differ diff --git a/public/assets/images/sort_asc.png b/public/assets/images/sort_asc.png new file mode 100644 index 0000000000..a88d7975fe Binary files /dev/null and b/public/assets/images/sort_asc.png differ diff --git a/public/assets/images/sort_asc_disabled.png b/public/assets/images/sort_asc_disabled.png new file mode 100644 index 0000000000..dcd7b7b8ca Binary files /dev/null and b/public/assets/images/sort_asc_disabled.png differ diff --git a/public/assets/images/sort_both.png b/public/assets/images/sort_both.png new file mode 100644 index 0000000000..18670406bc Binary files /dev/null and b/public/assets/images/sort_both.png differ diff --git a/public/assets/images/sort_desc.png b/public/assets/images/sort_desc.png new file mode 100644 index 0000000000..def071ed5a Binary files /dev/null and b/public/assets/images/sort_desc.png differ diff --git a/public/assets/images/sort_desc_disabled.png b/public/assets/images/sort_desc_disabled.png new file mode 100644 index 0000000000..7824973cc6 Binary files /dev/null and b/public/assets/images/sort_desc_disabled.png differ diff --git a/scripts/local-cleanup-reload.sh b/scripts/local-cleanup-reload.sh index 4021b6a336..673296e4d2 100755 --- a/scripts/local-cleanup-reload.sh +++ b/scripts/local-cleanup-reload.sh @@ -2,28 +2,29 @@ cd .. composer self-update composer update -cd scripts -rm -f ../app/storage/firefly-iii-import-*.json -rm -f ../app/storage/debugbar/*.json -rm -f ../app/storage/logs/larave*.log -rm -f ../app/storage/meta/services.json +rm -f ./app/storage/firefly-iii-import-*.json +rm -f ./app/storage/debugbar/*.json +rm -f ./app/storage/logs/larave*.log +rm -f ./app/storage/meta/services.json for i in `seq 0 9`; do - rm -f ../app/storage/views/$i* + rm -f ./app/storage/views/$i* done -rm -f ../app/storage/views/a* -rm -f ../app/storage/views/b* -rm -f ../app/storage/views/c* -rm -f ../app/storage/views/d* -rm -f ../app/storage/views/e* -rm -f ../app/storage/views/f* +rm -f ./app/storage/views/a* +rm -f ./app/storage/views/b* +rm -f ./app/storage/views/c* +rm -f ./app/storage/views/d* +rm -f ./app/storage/views/e* +rm -f ./app/storage/views/f* -php ../artisan clear-compiled --env=local -php ../artisan ide-helper:generate --env=local -php ../artisan ide-helper:models --env=local --write -php ../artisan optimize --env=local -php ../artisan dump-autoload --env=local + +php artisan clear-compiled --env=local +php artisan ide-helper:generate --env=local +php artisan ide-helper:models --env=local --write +php artisan optimize --env=local +php artisan dump-autoload --env=local +cd scripts ./local-reset.sh