Commit 7008c055 authored by Frank Bergmann's avatar Frank Bergmann

- timesheet basicly working, except duplification of entries

parent 199d1a8f
......@@ -17,6 +17,7 @@
<provides url="intranet-timesheet2" version="3.0.0"/>
<requires url="intranet-core" version="3.0.0"/>
<requires url="intranet-cost" version="3.0.0"/>
<requires url="intranet-timesheet2-tasks" version="3.0.0"/>
<requires url="intranet-material" version="3.0.0"/>
<callbacks>
......
......@@ -23,25 +23,30 @@
--
create table im_hours (
user_id integer not null
user_id integer
constraint im_hours_user_id_nn
not null
constraint im_hours_user_id_fk
references users,
project_id integer not null
project_id integer
constraint im_hours_project_id_nn
not null
constraint im_hours_project_id_fk
references im_projects,
timesheet_task_id integer
constraint im_hours_timesheet_task_id_nn
not null
constraint im_hours_timesheet_task_id_fk
references im_timesheet_tasks,
day timestamptz,
hours numeric(5,2),
-- ArsDigita/ACS billing system
-- ArsDigita/ACS billing system - log prices with hours
billing_rate numeric(5,2),
billing_currency char(3)
constraint im_hours_billing_currency_fk
references currency_codes(iso),
-- P/O billing system - leave prices to price list
material_id integer
constraint im_hours_material_fk
references im_materials,
note varchar(4000),
primary key(user_id, project_id, day)
primary key(user_id, project_id, timesheet_task_id, day)
);
create index im_hours_project_id_idx on im_hours(project_id);
......@@ -59,6 +64,11 @@ create table im_timesheet_tasks (
material_id integer
constraint im_timesheet_tasks_material_fk
references im_materials,
cost_center_id integer
constraint im_timesheet_tasks_cost_center_nn
not null
constraint im_timesheet_tasks_cost_center_fk
references im_cost_centers,
uom_id integer
constraint im_timesheet_tasks_uom_fk
references im_categories,
......@@ -71,220 +81,6 @@ create table im_timesheet_tasks (
);
---------------------------------------------------------
-- Timesheet Task Object Type
select acs_object_type__create_type (
'im_timesheet task', -- object_type
'Timesheet Task', -- pretty_name
'Timesheet Tasks', -- pretty_plural
'acs_object', -- supertype
'im_timesheet tasks', -- table_name
'timesheet task_id', -- id_column
'intranet-timesheet task', -- package_name
'f', -- abstract_p
null, -- type_extension_table
'im_timesheet task.name' -- name_method
);
create or replace function im_timesheet task__new (
integer,
varchar,
timestamptz,
integer,
varchar,
integer,
varchar,
varchar,
integer,
integer,
integer,
varchar
)
returns integer as '
declare
p_timesheet task_id alias for $1; -- timesheet task_id default null
p_object_type alias for $2; -- object_type default ''im_timesheet task''
p_creation_date alias for $3; -- creation_date default now()
p_creation_user alias for $4; -- creation_user
p_creation_ip alias for $5; -- creation_ip default null
p_context_id alias for $6; -- context_id default null
p_timesheet task_name alias for $7;
p_timesheet task_nr alias for $8;
p_timesheet task_type_id alias for $9;
p_timesheet task_status_id alias for $10;
p_timesheet task_uom_id alias for $11;
p_description alias for $12;
v_timesheet task_id integer;
begin
v_timesheet task_id := acs_object__new (
p_timesheet task_id, -- object_id
p_object_type, -- object_type
p_creation_date, -- creation_date
p_creation_user, -- creation_user
p_creation_ip, -- creation_ip
p_context_id, -- context_id
''t'' -- security_inherit_p
);
insert into im_timesheet tasks (
timesheet task_id,
timesheet task_name,
timesheet task_nr,
timesheet task_type_id,
timesheet task_status_id,
timesheet task_uom_id,
description
) values (
p_timesheet task_id,
p_timesheet task_name,
p_timesheet task_nr,
p_timesheet task_type_id,
p_timesheet task_status_id,
p_timesheet task_uom_id,
p_description
);
return v_timesheet task_id;
end;' language 'plpgsql';
-- Delete a single timesheet task (if we know its ID...)
create or replace function im_timesheet task__delete (integer)
returns integer as '
declare
p_timesheet task_id alias for $1; -- timesheet task_id
begin
-- Erase the timesheet task
delete from im_timesheet tasks
where timesheet task_id = p_timesheet task_id;
-- Erase the object
PERFORM acs_object__delete(p_timesheet task_id);
return 0;
end;' language 'plpgsql';
create or replace function im_timesheet task__name (integer)
returns varchar as '
declare
p_timesheet task_id alias for $1; -- timesheet task_id
v_name varchar(40);
begin
select timesheet task_nr
into v_name
from im_timesheet tasks
where timesheet task_id = p_timesheet task_id;
return v_name;
end;' language 'plpgsql';
---------------------------------------------------------
-- Setup the "Materials" main menu entry
--
create or replace function inline_0 ()
returns integer as '
declare
-- Menu IDs
v_menu integer;
v_main_menu integer;
v_admin_menu integer;
-- Groups
v_employees integer;
v_accounting integer;
v_senman integer;
v_companies integer;
v_freelancers integer;
v_proman integer;
v_admins integer;
BEGIN
select group_id into v_admins from groups where group_name = ''P/O Admins'';
select group_id into v_senman from groups where group_name = ''Senior Managers'';
select group_id into v_proman from groups where group_name = ''Project Managers'';
select group_id into v_accounting from groups where group_name = ''Accounting'';
select group_id into v_employees from groups where group_name = ''Employees'';
select group_id into v_companies from groups where group_name = ''Customers'';
select group_id into v_freelancers from groups where group_name = ''Freelancers'';
select menu_id
into v_admin_menu
from im_menus
where label=''project'';
v_menu := im_menu__new (
null, -- p_menu_id
''acs_object'', -- object_type
now(), -- creation_date
null, -- creation_user
null, -- creation_ip
null, -- context_id
''intranet-task'', -- package_name
''task'', -- label
''Tasks'', -- name
''/intranet-task/'', -- url
75, -- sort_order
v_admin_menu, -- parent_menu_id
null -- p_visible_tcl
);
PERFORM acs_permission__grant_permission(v_menu, v_admins, ''read'');
PERFORM acs_permission__grant_permission(v_menu, v_senman, ''read'');
-- PERFORM acs_permission__grant_permission(v_menu, v_proman, ''read'');
-- PERFORM acs_permission__grant_permission(v_menu, v_accounting, ''read'');
-- PERFORM acs_permission__grant_permission(v_menu, v_employees, ''read'');
-- PERFORM acs_permission__grant_permission(v_menu, v_companies, ''read'');
-- PERFORM acs_permission__grant_permission(v_menu, v_freelancers, ''read'');
return 0;
end;' language 'plpgsql';
select inline_0 ();
drop function inline_0 ();
-- Timesheet TaskList
--
delete from im_view_columns where column_id >= 90000 and column_id < 90099;
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90000,900,NULL,'Nr',
'"<a href=/intranet-timesheet task/new?[export_url_vars timesheet task_id return_url]>$timesheet task_nr</a>"',
'','',0,'');
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90002,900,NULL,'Name',
'"<a href=/intranet-timesheet task/new?[export_url_vars timesheet task_id return_url]>$timesheet task_name</a>"',
'','',2,'');
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90004,900,NULL,'Type',
'$timesheet task_type','','',4,'');
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90006,900,NULL,'Status',
'$timesheet task_status','','',6,'');
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90008,900,NULL,'UoM',
'$uom','','',8,'');
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl,
extra_select, extra_where, sort_order, visible_for) values (90010,900,NULL,
'Description', '$description', '','',10,'');
------------------------------------------------------
......
......@@ -23,128 +23,172 @@ ad_page_contract {
@author mbryzek@arsdigita.com
@author frank.bergmann@project-open.com
} {
hours:array,html
project_ids:array,integer
timesheet_task_ids:array,integer
hours:array
notes:array,html
julian_date
{ return_url "" }
}
# ----------------------------------------------------------
# Default & Security
# ----------------------------------------------------------
set user_id [ad_maybe_redirect_for_registration]
set user_name [db_string user_name "select first_names || ' ' || last_name from cc_users where user_id=:user_id" -default "User $user_id"]
set date_format "YYYY-MM-DD"
set currency [ad_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
set billing_rate 0
# Please note "_" instead of "-". This is because we use
# underscores in the invoices and other costs. So the
# timesheet information gets sorted in the right order.
set timesheet_log_date_format "YYYY_MM_DD"
set item_nrs [array names hours]
ns_log Notice "timesheet2-tasks/new-2: items_nrs=$item_nrs"
db_transaction {
foreach name [array names hours] {
if { ![regsub {\.hours$} $name "" on_what_id] } {
continue
}
set hours_worked $hours($name)
if { [empty_string_p $hours_worked] } {
set hours_worked 0
}
if { [info exists hours(${on_what_id}.note)] } {
set note [string trim $hours(${on_what_id}.note)]
} else {
set note ""
}
if { [info exists hours(${on_what_id}.billing_rate)] } {
# Explicitely stated billing rate
set billing_rate $hours(${on_what_id}.billing_rate)
} else {
set billing_rate ""
# Get the billing rate from the HR module
if {[db_table_exists im_employees]} {
db_1row houly_costs "
select
hourly_cost as billing_rate,
currency
from
im_employees
where
employee_id = :user_id"
}
if {"" == $billing_rate} { set billing_rate 0 }
}
foreach item_nr $item_nrs {
ns_log Notice "timesheet2-tasks/new-2: item_nr=$item_nr"
if { $hours_worked == 0 && [empty_string_p $note] } {
db_dml hours_delete "
# Extract the parameters from the arrays
set hours_worked $hours($item_nr)
set project_id $project_ids($item_nr)
set note $notes($item_nr)
set timesheet_task_id $timesheet_task_ids($item_nr)
if {"" == $project_id || 0 == $project_id} {
ad_return_complaint 1 "Internal Error:<br>
There is an empty project_id for item \# $item_nr"
return
}
# Filter & trim parameters
if { [empty_string_p $hours_worked] } {
set hours_worked 0
}
set note [string trim $note]
if { $hours_worked == 0 && [empty_string_p $note] } {
db_dml hours_delete "
delete from im_hours
where
project_id = :on_what_id
project_id = :project_id
and timesheet_task_id = :timesheet_task_id
and user_id = :user_id
and day = to_date(:julian_date, 'J')
"
"
if {[db_table_exists im_costs]} {
db_dml costs_delete "
db_dml costs_delete "
delete from im_costs
where
cost_type_id = [im_cost_type_timesheet]
and project_id = :on_what_id
and project_id = :project_id
and effective_date = to_date(:julian_date, 'J')
and cause_object_id = :user_id
"
}
and cause_object_id = :timesheet_task_id
"
# ad_return_complaint 1 "timesheet2-tasks/new-2: delete: hours_worked=$hours_worked, project_id=$project_id, timesheet_task_id=$timesheet_task_id"
} else {
} else {
if { [regexp {([0-9]+)(\,([0-9]+))?} $hours_worked] } {
regsub "," $hours_worked "." hours_worked
regsub "'" $hours_worked "." hours_worked
} elseif { [regexp {([0-9]+)(\'([0-9]+))?} $hours_worked] } {
regsub "'" $hours_worked "." hours_worked
regsub "," $hours_worked "." hours_worked
}
# Check if this entry is coming from a project without a
# timesheet task already defined:
if {"" == $timesheet_task_id || 0 == $timesheet_task_id} {
set timesheet_task_id [db_string existing_default_task "
select task_id
from im_timesheet_tasks
where project_id = :project_id
and task_nr = 'default'
" -default 0]
}
if { [regexp {([0-9]+)(\,([0-9]+))?} $hours_worked] } {
regsub "," $hours_worked "." hours_worked
regsub "'" $hours_worked "." hours_worked
} elseif { [regexp {([0-9]+)(\'([0-9]+))?} $hours_worked] } {
regsub "'" $hours_worked "." hours_worked
regsub "," $hours_worked "." hours_worked
if {"" == $timesheet_task_id || 0 == $timesheet_task_id} {
# Create a default timesheet task for this project
set task_id [im_new_object_id]
set task_nr "default"
set task_name "Default Task"
set material_id [db_string default_material "select material_id from im_materials where material_nr='default'" -default 0]
if {!$material_id} {
ad_return_complaint 1 "Configuration Error:<br>Error during the creation of a default timesheet task for project \#$project_id: We couldn't find any default material with the material_nr 'default'. <br>Please inform your system administrator or contact Project/Open."
}
set cost_center_id ""
set uom_id [im_uom_hour]
set task_type_id [im_timesheet_task_type_standard]
set task_status_id [im_timesheet_task_status_active]
set description "Default task for timesheet logging convenience - please update"
db_exec_plsql task_insert ""
# Update the hours table
#
db_dml hours_update "
}
# ad_return_complaint 1 "timesheet2-tasks/new-2: insert: hours_worked=$hours_worked, project_id=$project_id, timesheet_task_id=$timesheet_task_id"
# Update the hours table
#
db_dml hours_update "
update im_hours
set
hours = :hours_worked, note = :note,
billing_rate = :billing_rate
hours = :hours_worked,
note = :note
where
project_id = :on_what_id
project_id = :project_id
and timesheet_task_id = :timesheet_task_id
and user_id = :user_id
and day = to_date(:julian_date, 'J')
"
"
if { [db_resultrows] == 0 } {
db_dml hours_insert "
if { [db_resultrows] == 0 } {
db_dml hours_insert "
insert into im_hours (
user_id, project_id, day,
hours, billing_rate, note
user_id, project_id, timesheet_task_id,
day, hours, billing_rate, note
) values (
:user_id, :on_what_id, to_date(:julian_date,'J'),
:hours_worked, :billing_rate, :note
:user_id, :project_id, :timesheet_task_id,
to_date(:julian_date,'J'), :hours_worked, :billing_rate, :note
)"
}
set cost_id [db_string costs_id_exist "select cost_id from im_costs where cost_type_id = [im_cost_type_timesheet] and project_id = :on_what_id and effective_date = to_date(:julian_date, 'J') and cause_object_id = :user_id" -default 0]
}
set cost_id [db_string costs_id_exist "
select
cost_id
from
im_costs
where
cost_type_id = [im_cost_type_timesheet]
and project_id = :project_id
and effective_date = to_date(:julian_date, 'J')
and cause_object_id = :user_id
" -default 0]
set day [db_string day "select to_char(to_date(:julian_date, 'J'), :timesheet_log_date_format) from dual"]
set cost_name "$day $user_name"
if {!$cost_id} {
set cost_id [im_cost::new -cost_name $cost_name -cost_type_id [im_cost_type_timesheet]]
}
set day [db_string day "select to_char(to_date(:julian_date, 'J'), :timesheet_log_date_format) from dual"]
set cost_name "$day $user_name"
if {!$cost_id} {
set cost_id [im_cost::new -cost_name $cost_name -cost_type_id [im_cost_type_timesheet]]
}
set customer_id [db_string customer_id "select company_id from im_projects where project_id = :on_what_id" -default 0]
set customer_id [db_string customer_id "select company_id from im_projects where project_id = :project_id" -default 0]
# Update costs table
if {[db_table_exists im_costs]} {
# Update costs table
if {[db_table_exists im_costs]} {
db_dml cost_update "
update im_costs set
cost_name = :cost_name,
project_id = :on_what_id,
project_id = :project_id,
customer_id = :customer_id,
effective_date = to_date(:julian_date, 'J'),
amount = :billing_rate * cast(:hours_worked as numeric),
......@@ -152,18 +196,17 @@ db_transaction {
payment_days = 0,
vat = 0,
tax = 0,
cause_object_id = :user_id,
cause_object_id = :timesheet_task_id,
description = :note
where
cost_id = :cost_id
"
}
}
}
}
db_release_unused_handles
if { ![empty_string_p $return_url] } {
......
......@@ -16,7 +16,7 @@
<th colspan=99>Timesheet Filters</th>
</tr>
<tr>
<td>#intranet-core.Project#</td>
<td>#intranet-core.Project_name#</td>
<td><%= [im_project_select project_id $project_id_for_default] %></td>
<td>
<input type=submit value="Go">
......@@ -65,9 +65,9 @@
<table border=0 cellpadding=1 cellspacing=1>
<tr class=rowtitle>
<th>#intranet-timesheet2.Project_name#</th>
<th>#intranet-core.Unit#</th>
<th>#intranet-timesheet2.Hours# </th>
<th>#intranet-timesheet2.Work_done# </th>
<!-- <th>#intranet-timesheet2.Billing_rate#</th> -->
</tr>
@results;noquote@
<tr>
......
......@@ -158,23 +158,30 @@ select
t.uom_id,
t.planned_units,
t.reported_units_cache,
m.material_name,
children.project_id as project_id,
children.project_nr as project_nr,
children.project_name as project_name,
parent.project_id as parent_project_id,
children.parent_id as parent_project_id,
parent.project_nr as parent_project_nr,
parent.project_name as parent_project_name,
tree_level(children.tree_sortkey) - tree_level(parent.tree_sortkey) as subproject_level
tree_level(children.tree_sortkey) -1 as subproject_level
from
im_projects parent,
im_projects children
left outer join
im_timesheet_tasks t
on (children.project_id = t.project_id)
left outer join (
select *
from im_hours h
where h.day = to_date(:julian_date, 'J')
and h.user_id = :user_id
) h on (children.project_id = h.project_id)
left outer join im_timesheet_tasks t on (children.project_id = t.project_id)
select *
from im_hours h
where h.day = to_date(:julian_date, 'J')
and h.user_id = :user_id
) h
on (t.task_id = h.timesheet_task_id and h.project_id = children.project_id)
left outer join
im_materials m
on (t.material_id = m.material_id)
where
children.tree_sortkey between
parent.tree_sortkey and
......@@ -202,53 +209,90 @@ set old_project_id 0
db_foreach $statement_name $sql {
set indent ""
while {$subproject_level > 0} {
set level $subproject_level
while {$level > 0} {
set indent "$nbsps$indent"
set subproject_level [expr $subproject_level-1]
set level [expr $level-1]
}
# Insert intermediate header for every new project
if {$old_project_id != $project_id} {
# Add an empty line after every main project
if {"" == $parent_project_id} {
append results "<tr class=rowplain><td colspan=99>&nbsp;</td></tr>\n"
}
# Add a line for a project. This is useful if there are
# no timesheet_tasks yet for this project, because we
# always want to allow employees to log their ours in
# order not to give them excuses.
#
append results "
<tr $bgcolor([expr $ctr % 2])>
<td valign=top>
<nobr>
$indent
<A href=/intranet/projects/view?project_id=$project_id>
<B>$project_name</B>
</A>
</nobr>
</td>
<td valign=top>
<INPUT NAME=hours.${project_id}.hours size=5 MAXLENGTH=5 value=\"$hours\"]>
</td>
<td valign=top>
<INPUT NAME=hours.${project_id}.note size=60 value=\"[ns_quotehtml [value_if_exists note]]\">
</td>
</tr>\n"
<tr $bgcolor([expr $ctr % 2])>
<td>
<nobr>
$indent
<A href=/intranet/projects/view?project_id=$project_id>
<B>$project_name</B>
</A>
</nobr>
<input type=hidden name=\"project_ids.$ctr\" value=\"$project_id\">
<input type=hidden name=\"timesheet_task_ids.$ctr\" value=\"$task_id\">
</td>
"
if {"" == $task_name} {
append results "
<td>(nothing defined yet)</td>
<td>
<INPUT NAME=hours.$ctr size=5 MAXLENGTH=5 value=\"$hours\">
</td>
<td>
<INPUT NAME=notes.$ctr size=60 value=\"[ns_quotehtml [value_if_exists note]]\">
</td>
</tr>
"
} else {
append results "
<td></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
"
}
set old_project_id $project_id
incr ctr
}
append results "
<tr $bgcolor([expr $ctr % 2])>
<td valign=top>
<nobr>
$indent$nbsps
<A href=/intranet-timesheet2-tasks/view?[export_url_vars task_id]>
$task_name
</A>
</nobr>
</td>
<td valign=top>
<INPUT NAME=hours.${project_id}.hours size=5 MAXLENGTH=5 value=\"$hours\"]>
</td>
<td valign=top>
<INPUT NAME=hours.${project_id}.note size=60 value=\"[ns_quotehtml [value_if_exists note]]\">
</td>
</tr>
"
# Don't show the empty tasks that are produced with each project
# due to the "left outer join" SQL query
if {"" != $task_name} {
append results "
<tr $bgcolor([expr $ctr % 2])>
<td>
<nobr>
$indent$nbsps
<A href=/intranet-timesheet2-tasks/view?[export_url_vars task_id]>
$task_name
</A>
</nobr>
<input type=hidden name=\"project_ids.$ctr\" value=\"$project_id\">
<input type=hidden name=\"timesheet_task_ids.$ctr\" value=\"$task_id\">
</td>
<td>$material_name</td>
<td>
<INPUT NAME=hours.$ctr size=5 MAXLENGTH=5 value=\"$hours\">
</td>
<td>
<INPUT NAME=notes.$ctr size=60 value=\"[ns_quotehtml [value_if_exists note]]\">
</td>
</tr>
"
}
incr ctr
}
......
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