Commit 01672e8a authored by aecres's avatar aecres

- First - more or less - working version

parent f8b5dc2b
......@@ -2,18 +2,27 @@
<script type='text/javascript'>
Ext.require(['Ext.chart.*', 'Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit']);
Ext.onReady(function () {
revenueByDeptsStore = Ext.create('Ext.data.Store', {
fields: @header_json;noquote@,
data: [
@data;noquote@
]
autoLoad: true,
proxy: {
type: 'rest',
url: '/intranet-reporting-dashboard/revenue-by-dept.json',
extraParams: { // Parameters to the data-source
diagram_interval: 'last_year', //
diagram_fact: 'revenue', //
diagram_dept_sql: "@diagram_dept_sql;noquote@" //
},
reader: { type: 'json', root: 'data' }
}
});
var revenueByDeptsIntervalStore = 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.All_Time "All Time"]%>", "value":"all_time"},
{"display":"<%=[lang::message::lookup "" intranet-reporting-dashboard.Last_Two_Years "Last Two Year"]%>", "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"}
]
......@@ -26,7 +35,6 @@ Ext.onReady(function () {
legend: { position: 'right' },
insetPadding: 20,
theme: 'Base:gradients',
axes: [{
type: 'Numeric',
position: 'left',
......@@ -39,8 +47,6 @@ Ext.onReady(function () {
dateFormat: 'j M y',
constraint: false,
step: [Ext.Date.MONTH, 1],
// fromDate: new Date('2019-01-01'),
// toDate: new Date('2019-06-01'),
label: {rotate: {degrees: 315}}
}],
series: @series_list_json;noquote@
......@@ -65,7 +71,7 @@ Ext.onReady(function () {
triggerAction: 'all',
width: 150,
forceSelection: true,
value: 'all_time',
value: 'last_year',
listeners:{select:{fn:function(combo, comboValues) {
var value = comboValues[0].data.value;
var extraParams = revenueByDeptsStore.getProxy().extraParams;
......
......@@ -14,13 +14,26 @@
if {![info exists diagram_width]} { set diagram_width 600 }
if {![info exists diagram_height]} { set diagram_height 500 }
if {![info exists diagram_title]} { set diagram_title [lang::message::lookup "" intranet-reporting-dashboard.Revenue_by_Department "Revenue by Department"] }
if {![info exists diagram_dept_sql]} { set diagram_dept_sql "" }
if {![info exists diagram_intervall]} { set diagram_interval "last_year" }
if {![info exists diagram_fact]} { set diagram_fact "revenue" }
if {![info exists diagram_min_start_date]} { set diagram_min_start_date "2015-01-01" }
# Main classifier
# ----------------------------------------------------
# dept_sql - how to determine the department or area?
# ----------------------------------------------------
set default_diagram_dept_sql "coalesce((select cost_center_name from im_cost_centers where cost_center_id = project_cost_center_id), 'none')"
if {"" eq $diagram_dept_sql} {
set diagram_dept_sql $default_diagram_dept_sql
}
# Classifier
# set dept_sql "coalesce(acs_object__name(project_cost_center_id), 'none')"
# set dept_sql "coalesce(im_category_from_id(aec_area_id), 'none')"
#set dept_sql "coalesce((select category from im_categories where category_id = aec_area_id), 'none')"
set dept_sql "coalesce((select cost_center_name from im_cost_centers where cost_center_id = project_cost_center_id), 'none')"
# set dept_sql "coalesce((select cost_center_name from im_cost_centers where cost_center_id = project_cost_center_id), 'none')"
set diagram_dept_sql "coalesce((select category from im_categories where category_id = aec_area_id), 'none')"
# ----------------------------------------------------
# Diagram Setup
......@@ -31,84 +44,16 @@ set diagram_rand [expr {round(rand() * 100000000.0)}]
set diagram_id "revenu_by_dept_$diagram_rand"
set default_currency [im_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
set invoice_finished_period 30
# ----------------------------------------------------
# Revenues by department
#
# Note: Depending on the status and the time since the end
# of the project we have to take different formulas for value
# and costs:
# - Up to 30 days after closing the project there may be new
# invoices or provider bills
# - projects in status "closed lost" may have quotes, but
# should have to invoices, so "revenue" should be zero.
# ----------------------------------------------------
set middle_sql "
select main_p.project_id,
'<a href=/intranet/projects/view?project_id='||main_p.project_id||'>'||
main_p.project_name || '</a>' as name,
main_p.start_date,
main_p.end_date,
main_p.department,
revenue * now_percent / 100.0 / 1000.0 as now_revenue,
external_cost * now_percent / 100.0 / 1000.0 as now_external_cost,
internal_cost * now_percent / 100.0 / 1000.0 as now_internal_cost,
(revenue * now_percent - external_cost * now_percent - internal_cost * now_percent) / 100.0 / 1000.0 as now_profit
from
(select *,
round(CASE
WHEN (:now::date - end_date) > 0 THEN 100.0 -- project finished
WHEN (:now::date - start_date) < 0 THEN 0.0 -- not yet started
ELSE 100.0 * (:now::date - start_date) / (greatest(1, end_date - start_date)) -- in course
END,2) as now_percent
from (
select project_id,
project_name,
$dept_sql as department,
start_date::date as start_date,
greatest(end_date::date, start_date::date + (cost_invoices_cache/5000)::integer) as end_date,
project_status_id,
cost_invoices_cache as revenue,
cost_bills_cache as external_cost,
cost_timesheet_logged_cache + cost_expense_logged_cache as internal_cost
from im_projects
where parent_id is null and
end_date >= :first_project_start::date
) p
where 1=1
) main_p
where
1 = 1
-- order by profit ASC
-- order by revenue * now_percent - external_cost * now_percent - internal_cost * now_percent
"
# set first_project_start [db_string now "select now()::date - 365"]
# set now [db_string now "select now()::date"]
# ad_return_complaint 1 [im_ad_hoc_query -format html "$middle_sql"]
set revenue_sql "
select
department,
sum(now_revenue) as revenue,
sum(now_external_cost) as external_cost,
sum(now_internal_cost) as internal_cost,
sum(now_profit) as profit
from
($middle_sql) t
group by department
order by department
"
# Get the list of all departments
set dept_list [db_list dept_list "select dept from (select distinct $dept_sql as dept from im_projects) t order by lower(dept)"]
set dept_list [db_list dept_list "select dept from (select distinct $diagram_dept_sql as dept from im_projects) t order by lower(dept)"]
set dept_list_json "\['[join $dept_list "', '"]'\]"
# The header of the Sencha store:
set header_list [linsert $dept_list 0 "Date"]
set header_json "\['[join $header_list "', '"]'\]"
# Series JSON
set series_list {}
foreach dept $dept_list {
lappend series_list "{
......@@ -123,57 +68,3 @@ foreach dept $dept_list {
}"
}
set series_list_json "\[\n [join $series_list ",\n "]\n \]"
# Get the month dimension
set first_project_start [db_string first_project "select greatest(min(start_date), now()::date - 365*2) from im_projects" -default "2010-01-01"]
set months [db_list months "select * from im_month_enumerator(:first_project_start::date, now()::date)"]
# The header of the Sencha store:
set header_list [linsert $dept_list 0 "Date"]
set header_json "\['[join $header_list "', '"]'\]"
# ----------------------------------------------------
# Start looping
# ----------------------------------------------------
foreach dept $dept_list { set rev_hash_old($dept) 0.0 }
set rev_rows {}
foreach now $months {
set rev_line [list "'Date': new Date(\"$now\")"]
array unset rev_hash
db_foreach rev $revenue_sql {
set rev_hash($department) $revenue
set int_cost_hash($department) $internal_cost
set ext_cost_hash($department) $external_cost
set profit_hash($department) $profit
}
# Extract a list of revenues by dept, following the list of depts
foreach dept $dept_list {
set value 0.0; # current value
if {[info exists rev_hash($dept)]} { set value $rev_hash($dept) }
set old_value 0.0; # value from last month
if {[info exists rev_hash_old($dept)]} { set old_value $rev_hash_old($dept) }
set rev_hash_old($dept) $value; # update the old value for next iteration
set diff [expr round(1000.0 * ($value - $old_value)) / 1000.0]
lappend rev_line "'$dept': $diff"
}
lappend rev_rows "\{[join $rev_line ", "]\}"
}
# Join the rows together to a store
set data " [join $rev_rows ",\n "]"
# /packages/intranet-reporting-dashboard/www/revenue-by-dept-data-source.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-dept Sencha line chart.
<ul>
<li>diagram_interval options include "all_time", "last_year", "last_quarter" and "last_month"
<li>diagram_fact options include "revenue", "profit", "internal_cost" and "external_cost"
<li>diagram_dept_sql is a piece of SQL that extracts the department (or any other value)
from the im_projects table. Options include:
<ul>
<li>department: "coalesce((select cost_center_name from im_cost_centers where cost_center_id = project_cost_center_id), 'none')"
<li>project type: "coalesce(im_category_from_id(project_type_id), 'none')"
<li>area (assuming im_projects.area_id): "coalesce(im_category_from_id(area_id), 'none')"
</ul>
} {
{ diagram_interval "all_time" }
{ diagram_dept_sql "" }
{ diagram_fact "revenue" }
{ diagram_min_start_date "2015-01-01" }
}
# ----------------------------------------------------
# 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 200 "text/html" $json
ad_script_abort
}
set default_currency [im_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
set default_diagram_dept_sql "coalesce((select cost_center_name from im_cost_centers where cost_center_id = project_cost_center_id), 'none')"
if {"" eq $diagram_dept_sql} {
set diagram_dept_sql $default_diagram_dept_sql
}
# Check for hack attack
if {[regexp {\;\:} $diagram_dept_sql match]} {
im_security_alert -location "revenue-by-dept.json.tcl" -message "SQL injection attempt" -value $diagram_dept_sql
set diagram_dept_sql $default_diagram_dept_sql
}
# ----------------------------------------------------
#
# ----------------------------------------------------
switch $diagram_interval {
all_time { set diagram_start_date [db_string all_time "
select greatest(min(start_date)::date, :diagram_min_start_date::date) from im_projects where parent_id is null
"] }
last_year { set diagram_start_date [db_string year "select now()::date - 365 - 31"] }
last_two_years { set diagram_start_date [db_string year "select now()::date - 365*2 - 31"] }
last_quarter { set diagram_start_date [db_string year "select now()::date - 90 - 31"] }
default {
set json "{\"success\": false, \"message\": \"Invalid diagram_interval option: '$diagram_interval'.\" }"
doc_return 200 "text/html" $json
ad_script_abort
}
}
# ----------------------------------------------------
# <fact> by department
# ----------------------------------------------------
set middle_sql "
select main_p.project_id,
main_p.project_name,
main_p.start_date,
main_p.end_date,
main_p.department,
revenue * now_percent / 100.0 / 1000.0 as now_revenue,
external_cost * now_percent / 100.0 / 1000.0 as now_external_cost,
internal_cost * now_percent / 100.0 / 1000.0 as now_internal_cost,
(revenue * now_percent - external_cost * now_percent - internal_cost * now_percent) / 100.0 / 1000.0 as now_profit
from
(select *,
round(CASE
WHEN (:now::date - end_date) > 0 THEN 100.0 -- project finished
WHEN (:now::date - start_date) < 0 THEN 0.0 -- not yet started
ELSE 100.0 * (:now::date - start_date) / (greatest(1, end_date - start_date)) -- in course
END,2) as now_percent
from (
select project_id,
project_name,
$diagram_dept_sql as department,
start_date::date as start_date,
-- Handle the case of projects with bad start- and end_date
greatest(end_date::date, start_date::date + (cost_invoices_cache/5000)::integer) as end_date,
project_status_id,
cost_invoices_cache as revenue,
cost_bills_cache as external_cost,
cost_timesheet_logged_cache + cost_expense_logged_cache as internal_cost
from im_projects
where parent_id is null and
start_date is not null and
end_date >= :diagram_start_date::date
) p
) main_p
"
# set now [db_string now "select now()::date"]
# ad_return_complaint 1 [im_ad_hoc_query -format html "$middle_sql"]
set revenue_sql "
select
department,
sum(now_revenue) as revenue,
sum(now_external_cost) as external_cost,
sum(now_internal_cost) as internal_cost,
sum(now_profit) as profit
from
($middle_sql) t
group by department
order by department
"
# ----------------------------------------------------
# Dimensions
# ----------------------------------------------------
# Get the list of all departments
set dept_list [db_list dept_list "select dept from (select distinct $diagram_dept_sql as dept from im_projects) t order by lower(dept)"]
# Get the month dimension
set months [db_list months "select * from im_month_enumerator(:diagram_start_date::date, now()::date)"]
# ----------------------------------------------------
# Start looping
# ----------------------------------------------------
foreach dept $dept_list { set rev_hash_old($dept) 0.0 }
set rev_rows {}
set cnt 0
foreach now $months {
set rev_line [list "'Date': new Date(\"$now\")"]
array unset rev_hash
db_foreach rev $revenue_sql {
set rev_hash($department) $revenue
set int_cost_hash($department) $internal_cost
set ext_cost_hash($department) $external_cost
set profit_hash($department) $profit
}
# Extract a list of revenues by dept, following the list of depts
foreach dept $dept_list {
set value 0.0; # current value
if {[info exists rev_hash($dept)]} { set value $rev_hash($dept) }
set old_value 0.0; # value from last month
if {[info exists rev_hash_old($dept)]} { set old_value $rev_hash_old($dept) }
set rev_hash_old($dept) $value; # update the old value for next iteration
set diff [expr round(1000.0 * ($value - $old_value)) / 1000.0]
lappend rev_line "'$dept': $diff"
}
# Skip the first row, because it starts with 0
if {$cnt > 0} {
lappend rev_rows "\{[join $rev_line ", "]\}"
}
incr cnt
}
# ----------------------------------------------------
# Create JSON for data source
# ----------------------------------------------------
set json "{\"success\": true, \"message\": \"Data loaded\", \"data\": \[\n[join $rev_rows ",\n"]\n\]}"
doc_return 200 "text/html" $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