Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
I
intranet-reporting-dashboard
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
project-open
intranet-reporting-dashboard
Commits
01672e8a
Commit
01672e8a
authored
Apr 10, 2019
by
aecres
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- First - more or less - working version
parent
f8b5dc2b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
229 additions
and
139 deletions
+229
-139
revenue-by-dept.adp
lib/revenue-by-dept.adp
+15
-9
revenue-by-dept.tcl
lib/revenue-by-dept.tcl
+21
-130
revenue-by-dept.json.tcl
www/revenue-by-dept.json.tcl
+193
-0
No files found.
lib/revenue-by-dept.adp
View file @
01672e8a
...
...
@@ -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;
...
...
lib/revenue-by-dept.tcl
View file @
01672e8a
...
...
@@ -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
$d
iagram
_d
ept_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
"
]
"
www/revenue-by-dept.json.tcl
0 → 100644
View file @
01672e8a
# /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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment