Commit 35d601da authored by Frank Bergmann's avatar Frank Bergmann

- Added portlet for revenue by month indicator

parent d7e36b4d
<div id="@diagram_id@" style="height: @diagram_height@px; width: @diagram_width@px"></div>
<script type='text/javascript'>
Ext.require(['Ext.chart.*', 'Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit']);
Ext.onReady(function () {
// The store with revenue by department data
var revenueByYearsStore = Ext.create('Ext.data.Store', {
fields: @header_json;noquote@,
autoLoad: false, // load handled manually
proxy: {
type: 'rest',
url: '/intranet-reporting-dashboard/revenue-by-year-monthly.json', // data-source for this indicator
extraParams: { // Parameters to the data-source
diagram_interval: '@diagram_default_interval@', // default interval to start with ('all_time')
diagram_fact: '@diagram_default_fact@' // Revenue or profit?
},
reader: { type: 'json', root: 'data' }
},
data: [ // Dummy content for store to avoid slow display since 1970-01-01...
{'month': 'month_01', 'invoices_2017': 100}
]
});
// value range for time interval select box
var revenueByYearsIntervalStore = Ext.create('Ext.data.Store', {
fields: ['display', 'value'],
data: [
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.All_Time "All Time"]%>", "value":"all_time"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Last_Two_Years "Last Two Years"]%>", "value":"last_two_years"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Last_Year "Last Year"]%>", "value":"last_year"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Last_Quarter "Last Quarter"]%>", "value":"last_quarter"}
]
});
// value range for what to show (revenue vs. profit)
var revenueByYearsFactStore = Ext.create('Ext.data.Store', {
fields: ['display', 'value'],
data: [
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Revenue "Revenue"]%>", "value":"revenue"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Profit "Profit"]%>", "value":"profit"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Internal_Cost "Internal Cost"]%>", "value":"internal_cost"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.External_Cost "External Cost"]%>", "value":"external_cost"}
]
});
// simple diagram...
revenueByYearsChart = new Ext.chart.Chart({
xtype: 'chart',
animate: false,
store: revenueByYearsStore,
legend: { position: 'right' },
theme: 'Base:gradients',
axes: [{
type: 'Numeric',
position: 'left',
fields: @year_list_json;noquote@,
title: 'Revenue (x 1000 @default_currency@)'
}, {
title: false,
type: 'Category',
position: 'bottom',
fields: ['month'],
label: {
rotate: {degrees: 315},
renderer: function(v) {
return Ext.Date.format(v, 'M y');
}
}
}],
series: @series_list_json;noquote@
});
// Window around diagram with drop select boxes that reload data when operated
var revenueByYearsPanel = Ext.create('widget.panel', {
width: @diagram_width@,
height: @diagram_height@,
title: '@diagram_title@',
renderTo: '@diagram_id@',
layout: 'fit',
header: false,
tbar: [
{
xtype: 'combo',
editable: false,
fieldLabel: '<%=[lang::message::lookup "" intranet-reporting-dashboard.Interval Interval]%>',
store: revenueByYearsIntervalStore,
mode: 'local',
displayField: 'display',
valueField: 'value',
triggerAction: 'all',
width: 250,
forceSelection: true,
value: '@diagram_default_interval@',
listeners:{select:{fn:function(combo, comboValues) {
var value = comboValues[0].data.value;
var extraParams = revenueByYearsStore.getProxy().extraParams;
extraParams.diagram_interval = value;
revenueByYearsStore.load();
}}}
}, '->',
{
xtype: 'combo',
editable: false,
fieldLabel: '<%=[lang::message::lookup "" intranet-reporting-dashboard.Fact_Dimension "Fact Dimension"]%>',
store: revenueByYearsFactStore,
mode: 'local',
displayField: 'display',
valueField: 'value',
triggerAction: 'all',
width: 250,
forceSelection: true,
value: '@diagram_default_fact@',
listeners:{select:{fn:function(combo, comboValues) {
var value = comboValues[0].data.value;
var extraParams = revenueByYearsStore.getProxy().extraParams;
extraParams.diagram_fact = value;
revenueByYearsStore.load();
}}}
}
],
items: revenueByYearsChart
});
revenueByYearsStore.load();
});
</script>
<!--
Ext.require(['Ext.chart.*', 'Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit']);
Ext.onReady(function () {
// value range for time interval select box
var revenueByMonthsIntervalStore = Ext.create('Ext.data.Store', {
fields: ['display', 'value'],
data: [
{"display":"All Time", "value":"all_time"},
{"display":"Last Two Years", "value":"last_two_years"},
{"display":"Last Year", "value":"last_year"},
{"display":"Last Quarter", "value":"last_quarter"}
]
});
// value range for what to show (revenue vs. profit)
var revenueByMonthsFactStore = Ext.create('Ext.data.Store', {
fields: ['display', 'value'],
data: [
{"display":"Revenue", "value":"revenue"},
{"display":"Internal Cost", "value":"internal_cost"},
{"display":"External Cost", "value":"external_cost"}
]
});
// simple diagram...
revenueByMonthsChart = new Ext.chart.Chart({
xtype: 'chart',
animate: false,
store: revenueByMonthsStore,
legend: { position: 'right' },
theme: 'Base:gradients',
axes: [{
type: 'Numeric',
position: 'left',
fields: ['2017', '2018', '2019'],
title: 'x 1000 EUR'
}, {
title: false,
type: 'Category',
position: 'bottom',
fields: ['Month'],
label: {rotate: {degrees: 315} }
}],
series: [
{
type: 'line',
title: '2017',
xField: 'Month', yField: '2017',
axis: 'left',
highlight: {size: 7, radius: 7}
},
{
type: 'line',
title: '2018',
xField: 'Month', yField: '2018',
axis: 'left',
highlight: {size: 7, radius: 7}
},
{
type: 'line',
title: '2019',
xField: 'Month', yField: '2019',
axis: 'left',
highlight: {size: 7, radius: 7}
}
]
});
// Window around diagram with drop-down boxes that reload data when operated
var revenueByMonthsPanel = Ext.create('widget.panel', {
width: 600,
height: 500,
title: 'Revenue by Month',
renderTo: 'revenu_by_year_80939009',
layout: 'fit',
header: false,
tbar: [
{
xtype: 'combo',
editable: false,
fieldLabel: 'Interval',
store: revenueByMonthsIntervalStore,
mode: 'local',
displayField: 'display',
valueField: 'value',
triggerAction: 'all',
width: 250,
forceSelection: true,
value: 'last_year',
listeners:{select:{fn:function(combo, comboValues) {
var value = comboValues[0].data.value;
var extraParams = revenueByMonthsStore.getProxy().extraParams;
extraParams.diagram_interval = value;
revenueByMonthsStore.load();
}}}
}, '->',
{
xtype: 'combo',
editable: false,
fieldLabel: 'Fact Dimension',
store: revenueByMonthsFactStore,
mode: 'local',
displayField: 'display',
valueField: 'value',
triggerAction: 'all',
width: 250,
forceSelection: true,
value: 'revenue',
listeners:{select:{fn:function(combo, comboValues) {
var value = comboValues[0].data.value;
var extraParams = revenueByMonthsStore.getProxy().extraParams;
extraParams.diagram_fact = value;
revenueByMonthsStore.load();
}}}
}
],
items: revenueByMonthsChart
});
revenueByMonthsStore.load();
});
</script>
</div>
-->
# /packages/intranet-reporting-dashboard/lib/revenue-per-month.tcl
#
# Copyright (C) 2019 ]project-open[
#
# All rights reserved. Please check
# http://www.project-open.com/license/ for details.
# ----------------------------------------------------------------------
# Variables
# ---------------------------------------------------------------------
# The following variables are expected in the environment
# defined by the calling /tcl/*.tcl libary:
if {![info exists diagram_width]} { set diagram_width 600 }
if {![info exists diagram_height]} { set diagram_height 500 }
if {![info exists diagram_title] || "" eq $diagram_title} { set diagram_title [lang::message::lookup "" intranet-reporting-dashboard.Revenue_by_Department "Revenue by Department"] }
if {![info exists diagram_default_interval] || "" eq $diagram_default_interval} { set diagram_default_interval "last_year" }
if {![info exists diagram_default_fact] || "" eq $diagram_default_fact} { set diagram_default_fact "revenue" }
if {![info exists diagram_min_start_date]} { set diagram_min_start_date "2015-01-01" }
# ----------------------------------------------------
# Diagram Setup
# ----------------------------------------------------
# Create a random ID for the diagram
set diagram_rand [expr {round(rand() * 100000000.0)}]
set diagram_id "revenue_by_month_$diagram_rand"
set default_currency [im_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
# Get the list of all years
set year_list [db_list year_list "select distinct 'invoices_' || to_char(im_month_enumerator, 'YYYY') as year from im_month_enumerator(:diagram_min_start_date::date, now()::date) order by year"]
set year_list_json "\['[join $year_list "', '"]'\]"
# The header of the Sencha store:
set header_list [linsert $year_list 0 "month"]
set header_json "\['[join $header_list "', '"]'\]"
# Series JSON
set series_list {}
foreach year $year_list {
lappend series_list "{
type: 'line',
title: '$year',
xField: 'month', yField: '$year',
axis: 'left',
highlight: {size: 7, radius: 7},
listeners: {
'itemclick': function(item, event) {
var date = item.storeItem.get('Year').toISOString().substring(0,10);
var value = item.value\[1\];
var year = item.series.yField;
var url = '/intranet-reporting-dashboard/revenue-by-year-monthly-details';
url = url + '?month='+month+'&year='+year;
window.open(url, '_blank');
}
},
tips: { width: 200, renderer: function(storeItem, item) {
this.setTitle('$year:<br>Year: '+storeItem.get('Year').toISOString().substring(0,7)+',<br> Revenues: '+storeItem.get('$year'));
}}
}"
}
set series_list_json "\[\n [join $series_list ",\n "]\n \]"
......@@ -550,3 +550,26 @@ SELECT acs_permission__grant_permission(
'read'
);
-- Revenues by year over months
--
SELECT im_component_plugin__new (
null, 'im_component_plugin', now(), null, null, null,
'Revenue by Year', -- plugin_name
'intranet-reporting-dashboard', -- package_name
'left', -- location
'/intranet-invoices/dashboard', -- page_url
null, -- view_name
130, -- sort_order
'im_dashboard_revenue_by_year_monthly -diagram_width 600 -diagram_height 500',
'lang::message::lookup "" intranet-reporting-dashboard.Revenue_by_Year_over_Months "Revenue by Year"'
);
SELECT acs_permission__grant_permission(
(select plugin_id from im_component_plugins where plugin_name = 'Revenue by Year'),
(select group_id from groups where group_name = 'Senior Managers'),
'read'
);
......@@ -914,3 +914,39 @@ ad_proc -public im_dashboard_revenue_by_dept {
# ---------------------------------------------------------------
# Revenues by month over year
# ---------------------------------------------------------------
ad_proc -public im_dashboard_revenue_by_year_monthly {
{-diagram_width 600}
{-diagram_height 500}
{-diagram_title ""}
{-diagram_default_interval "3" }
{-diagram_default_fact "revenue" }
} {
Returns a HTML component with a timeline of a revenues by month
} {
# Sencha check and permissions
if {![im_sencha_extjs_installed_p]} { return "" }
set current_user_id [ad_conn user_id]
if {![im_permission $current_user_id view_finance]} { return "You don't have the right to see financial data" }
im_sencha_extjs_load_libraries
# Call the portlet page
set params [list \
[list diagram_width $diagram_width] \
[list diagram_height $diagram_height] \
[list diagram_title $diagram_title] \
[list diagram_default_interval $diagram_default_interval] \
[list diagram_default_fact $diagram_default_fact] \
]
set result [ad_parse_template -params $params "/packages/intranet-reporting-dashboard/lib/revenue-by-year-monthly"]
return [string trim $result]
}
# /packages/intranet-reporting-dashboard/www/revenue-by-year-monthly.tcl
#
# Copyright (C) 2019 ]project-open[
#
# All rights reserved. Please check
# http://www.project-open.com/license/ for details.
# ----------------------------------------------------------------------
#
# ---------------------------------------------------------------------
ad_page_contract {
Datasource for revenues-by-year-monthly Sencha line chart.
<ul>
<li>diagram_interval sets the number of years to include
<li>diagram_fact options include "revenue", "profit", "internal_cost" and "external_cost"
</ul>
} {
{ diagram_interval "3" }
{ diagram_fact "revenue" }
}
# ----------------------------------------------------
# Defaults & Permissions
# ----------------------------------------------------
set current_user_id [ad_conn user_id]
if {![im_permission $current_user_id view_companies_all] || ![im_permission $current_user_id view_finance]} {
set json "{\"success\": false, \"message\": \"Insufficient permissions - you need view_companies_all and view_finance.\" }"
doc_return 400 "application/json" $json
ad_script_abort
}
set default_currency [im_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
set today_year [db_string today "select to_char(now(), 'YYYY')"]
set start_year [expr $today_year - $diagram_interval]
set today_date "$today_year-01-01"
set start_date "$start_year-01-01"
set next_start_year [expr $start_year + 1]
set next_start_date "$next_start_year-01-01"
set months_sql "select distinct to_char(im_month_enumerator, 'MM') as month from im_month_enumerator('$start_date'::date, '$next_start_date'::date) order by month"
set months [db_list months $months_sql]
# ----------------------------------------------------
# <fact> by department
# ----------------------------------------------------
set year_sql_list [list]
for {set year $start_year} {$year <= $today_year} {incr year} {
lappend year_sql_list "
,( select coalesce(sum(c.amount), 0.0)
from im_costs c
where c.cost_type_id = 3700 and
to_char(c.effective_date, 'YYYY') = '$year' and
to_char(c.effective_date, 'MM') = months.month
) as invoices_$year"
}
set sql "
select 'month_' || months.month || '' as month
[join $year_sql_list "\n"]
from ($months_sql) months
"
# ad_return_complaint 1 [im_ad_hoc_query -format html "$sql"]
# ----------------------------------------------------
# Create JSON for data source
# ----------------------------------------------------
set json [im_sencha_sql_to_store -data_source_p 1 -sql $sql]
doc_return 200 "application/json" $json
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment