Sales statistics

[#if salesreport.dateIntervalStart??] [#if salesreport.dateIntervalEnd??] ${salesreport.dateIntervalStart?date} - ${salesreport.dateIntervalEnd?date} [#else] After ${salesreport.dateIntervalStart?date} [/#if] [#else] [#if salesreport.dateIntervalEnd??] Before ${salesreport.dateIntervalEnd?date} [#else] All dates [/#if] [/#if]



[#if salesreport.invoices?size == 0]

There is no data available for the selected report settings.

[#else]

[#assign grandTotal=salesreport.calculateGrandTotal(salesreport.invoices)] [#assign balance=salesreport.calculateBalance(salesreport.invoices)] [#assign months=salesreport.groupDatesByMonth(salesreport.invoices)] [#assign clients=salesreport.groupClients(salesreport.invoices)] [#assign clientsMap = []] [#list clients as group] [#assign groupInvoices = salesreport.filterByClient(salesreport.invoices, group)] [#assign groupGrandTotal = salesreport.calculateGrandTotal(groupInvoices)] [#assign clientsMap = clientsMap + [{"client" : group, "percentage": (groupGrandTotal.contents()?first.amount / grandTotal.contents()?first.amount), "grandTotal": groupGrandTotal}]] [/#list] [#assign clientsBalanceMap = []] [#list clients as group] [#assign groupInvoices = salesreport.filterByClient(salesreport.invoices, group)] [#assign groupBalance = salesreport.calculateBalance(groupInvoices)] [#if !groupBalance.contents()?first.zero] [#assign clientsBalanceMap = clientsBalanceMap + [{"client" : group, "percentage": (groupBalance.contents()?first.amount / balance.contents()?first.amount), "balance": groupBalance}]] [/#if] [/#list] [#-- 1. Sales stats --]
Sales Months Avg. Sales / Month
${grandTotal} ${months?size?string("#")} ${grandTotal.contents()?first.amount / months?size}
[#-- 2. Unpaid invoices stats --] [#assign dueInvoices = []] [#assign overdueInvoices = []] [#list salesreport.invoices as invoice] [#if !invoice.paid] [#if invoice.overdue] [#assign overdueInvoices = overdueInvoices + [invoice]] [#else] [#assign dueInvoices = dueInvoices + [invoice]] [/#if] [/#if] [/#list] [#assign duePayments=salesreport.calculateBalance(dueInvoices)] [#assign overduePayments=salesreport.calculateBalance(overdueInvoices)] [#assign paymentsTotal=salesreport.calculatePaymentsTotal(salesreport.invoices)] [#assign paymentsTotalChartProperties = { 'title.visible': false, 'legend.visible': true, 'legend.position': 'right', 'legend.verticalAlignment': 'top', 'legend.frame': {"insets": [0, 0, 0, 0]}, 'legend.margin': [5, 0, 0, 0], 'legend.itemLabelPadding': [25, 15, 0, 5], 'legend.legendItemGraphicPadding': [25, 0, 0, 0], 'plot.legendLabelGenerator': '{0} (${grandTotal.contents()?first.currency.symbol}{1})', 'plot.backgroundPaint': '', 'plot.outlineVisible': false, 'plot.shadowPaint': '', 'plot.sectionOutlineStroke': [{'width': 2.0}, {'width': 2.0}, {'width': 2.0}], 'plot.sectionOutlinePaint': ['#109618', '#FF9900', '#DC3912'], 'plot.sectionPaint':['#109618', '#FF9900', '#DC3912'], 'plot.startAngle': 90, 'plot.labelGenerator': '', 'plot.centerTextMode': 'fixed', 'plot.centerText': '${(paymentsTotal.contents()?first.amount/grandTotal.contents()?first.amount)?string.percent}', 'plot.centerTextFont': {'size': 36}, 'plot.centerTextColor': '#000000', 'plot.sectionDepth': 0.10, 'plot.separatorsVisible': false } /] [#assign paymentsTotalDataSet] [ ['Paid', paymentsTotal.contents()?first.amount], ['Due', duePayments.contents()?first.amount], ['Overdue', overduePayments.contents()?first.amount] ] [/#assign] [#-- jfreechart work-around--] [#-- make sure the first value in the dataset is not zero because the center text won't be displayed --] [#if paymentsTotal.contents()?first.amount == 0] [#if duePayments.contents()?first.amount != 0] [#assign paymentsTotalDataSet] [ ['Due', duePayments.contents()?first.amount], ['Overdue', overduePayments.contents()?first.amount], ['Paid', paymentsTotal.contents()?first.amount] ] [/#assign] [#assign paymentsTotalChartProperties = paymentsTotalChartProperties + { 'plot.sectionOutlinePaint': ['#FF9900', '#DC3912', '#109618'], 'plot.sectionPaint':['#FF9900', '#DC3912', '#109618'] }] [#else] [#assign paymentsTotalDataSet] [ ['Overdue', overduePayments.contents()?first.amount], ['Due', duePayments.contents()?first.amount], ['Paid', paymentsTotal.contents()?first.amount] ] [/#assign] [#assign paymentsTotalChartProperties = paymentsTotalChartProperties + { 'plot.sectionOutlinePaint': ['#DC3912', '#FF9900', '#109618'], 'plot.sectionPaint':['#DC3912', '#FF9900', '#109618'] }] [/#if] [/#if]
Paid amount
[#-- 3. The bar chart that summarize sales by month --] [#-- Create a list with all the month names --] [#assign monthNames = []] [#list 1..12 as month] [#assign monthNames = monthNames + ["2000-${month?string('00')}-01"?date("yyyy-MM-dd")?string("MMM")]] [/#list] [#-- Build the data set expression record by record --] [#assign allInvoices=salesreport.invoices] [#assign years=salesreport.groupDatesByYear(allInvoices)] [#assign maxGrandTotal = 0] [#assign monthlySalesDataSet] [ ['Month' [#list years?sort as year] , '${year}' [/#list] ], [#list monthNames as monthName] ['${monthName}' [#list years?sort as year] [#assign yearInvoices=salesreport.filterByDate(allInvoices, year.toInterval())] [#assign monthsYear=salesreport.groupDatesByMonth(yearInvoices)] [#assign monthGrandTotal = 0] [#list monthsYear as month] [#if monthName == '${month.toString("MMM")}'] [#assign monthInvoices=salesreport.filterByDate(yearInvoices, month.toInterval())] [#assign monthGrandTotal=salesreport.calculateGrandTotal(monthInvoices).contents()?first.amount] [#if monthGrandTotal > maxGrandTotal][#assign maxGrandTotal = monthGrandTotal][/#if] [/#if] [/#list] , ${monthGrandTotal?c} [/#list] ] [#if monthName?has_next],[/#if] [/#list] ] [/#assign] [#assign monthlySalesChartProperties = { 'title.visible': false, 'legend.visible': true, 'legend.position': 'bottom', 'legend.verticalAlignment': 'top', 'legend.frame': {'insets': [0, 0, 0, 0]}, 'legend.margin': [0, 0, 20, 0], 'legend.itemLabelPadding': [5, 5, 5, 20], 'legend.legendItemGraphicPadding': [0, 0, 0, 0], 'plot.backgroundPaint': '', 'plot.outlineVisible': false, 'plot.axisOffset': [0, 0, 0, 0], 'plot.rangeAxis.visible': true, 'plot.rangeAxis.axisLineVisible': false, 'plot.rangeAxis.tickMarksVisible': false, 'plot.rangeAxis.labelFont': {'size': 12}, 'plot.rangeGridlinesVisible': true, 'plot.rangeGridlinePaint': '#aaaaaa', 'padding': [10, 10, 10, 10] } /]
Sales by month
[#-- 4. The pie chart summarize sales by client --] [#assign topPieChartProperties = { 'title.visible': false, 'legend.visible': false, 'plot.backgroundPaint': '', 'plot.outlineVisible': false, 'plot.shadowPaint': '', 'plot.startAngle': 90, 'plot.labelFont': {'size': 12}, 'plot.labelGenerator': '{0} ({2})', 'plot.labelBackgroundPaint': '', 'plot.labelShadowPaint': '', 'plot.labelOutlinePaint': '', 'plot.labelLinkStyle': 'standard', 'plot.maximumLabelWidth': 0.3, 'plot.sectionDepth': 0.25, 'plot.separatorsVisible': false } /] [#-- create datasets for the top elements --] [#assign clientSalesDataSet] [ [#if clients?size > 5] [#assign others = grandTotal.contents()?first.amount] [#list clientsMap?sort_by('percentage')?reverse?chunk(4)?first as client] ['${client['client'].name?js_string}', ${client['grandTotal'].contents()?first.amount?c}] , [#assign others = others - client['grandTotal'].contents()?first.amount] [/#list] ['Others', ${others?c}] [#else] [#list clientsMap?sort_by('grandTotal')?reverse as client] ['${client['client'].name?js_string}', ${client['grandTotal'].contents()?first.amount?c}] [#if client?has_next],[/#if] [/#list] [/#if] ] [/#assign] [#-- make sure the label of the last pie section is on the right so that the chart is less crowded --] [#assign clientSalesChartProperties = topPieChartProperties + { 'plot.startAngle': ((90 - ((clientSalesDataSet?eval[clientSalesDataSet?eval?size - 1][1] / grandTotal.contents()?first.amount) * 360 / 2)) - 1)?int }/]
Top Clients
[#-- 5. The table that summarizes sales by client --]

Sales by client

[#list clientsMap?sort_by('percentage')?reverse as client]

${(client_index + 1)?string("#")}.

${client['client'].name}

${client['percentage']?string("0%")}

${client['grandTotal']}

[/#list]

[#if clientsBalanceMap?size != 0]

[#-- 6. The bar chart that summarize outstanding payments by age --] [#assign unpaid30days = []] [#assign unpaid60days = []] [#assign unpaid90days = []] [#assign unpaid90daysplus = []] [#list salesreport.invoices as invoice] [#if !invoice.paid] [#if invoice.ageInDays < 30] [#assign unpaid30days = unpaid30days + [invoice]] [#elseif invoice.ageInDays < 60] [#assign unpaid60days = unpaid60days + [invoice]] [#elseif invoice.ageInDays < 90] [#assign unpaid90days = unpaid90days + [invoice]] [#else] [#assign unpaid90daysplus = unpaid90daysplus + [invoice]] [/#if] [/#if] [/#list] [#assign invoiceAgingDataSet] [ ['Age', 'Balance'], ['0-30 days', ${salesreport.calculateBalance(unpaid30days).contents()?first.amount?c}], ['30-60 days', ${salesreport.calculateBalance(unpaid60days).contents()?first.amount?c}], ['60-90 days', ${salesreport.calculateBalance(unpaid90days).contents()?first.amount?c}], ['90 days or more', ${salesreport.calculateBalance(unpaid90daysplus).contents()?first.amount?c}] ] [/#assign] [#assign chartProperties2 = { 'title.visible': false, 'legend.visible': false, 'plot.backgroundPaint': '', 'plot.outlineVisible': false, 'plot.axisOffset': [0, 0, 0, 0], 'plot.renderer.maximumBarWidth': 0.07, 'plot.rangeAxis.visible': true, 'plot.rangeAxis.axisLineVisible': false, 'plot.rangeAxis.tickMarksVisible': false, 'plot.rangeAxis.labelFont': {'size': 12}, 'plot.rangeGridlinesVisible': true, 'plot.rangeGridlinePaint': '#aaaaaa', 'padding': [10, 10, 10, 10] } /]
Unpaid amount by age
[#-- 7. The pie chart shows top outstanding clients --] [#assign topOutstandingClientsDataSet] [ [#if clientsBalanceMap?size > 5] [#assign others = balance.contents()?first.amount] [#list clientsBalanceMap?sort_by('percentage')?reverse?chunk(4)?first as client] ['${client['client'].name?js_string}', ${client['balance'].contents()?first.amount?c}] , [#assign others = others - client['balance'].contents()?first.amount] [/#list] ['Others', ${others?c}] [#else] [#list clientsBalanceMap?sort_by('balance')?reverse as client] ['${client['client'].name?js_string}', ${client['balance'].contents()?first.amount?c}] [#if client?has_next],[/#if] [/#list] [/#if] ] [/#assign] [#-- make sure the label of the last pie section is on the right so that the chart is less crowded --] [#assign topOutstandingClientsPieChartProperties = topPieChartProperties + { 'plot.startAngle': ((90 - ((topOutstandingClientsDataSet?eval[topOutstandingClientsDataSet?eval?size - 1][1] / balance.contents()?first.amount) * 360 / 2)) - 1)?int }/]
Top Outstanding Clients
[#-- 8. The table that shows outstanding clients --]

Outstanding clients

[#list clientsBalanceMap?sort_by('percentage')?reverse as client]

${(client_index + 1)?string("#")}.

${client['client'].name}

${client['percentage']?string("0%")}

${client['balance']}

[/#list]

[/#if]

[/#if]