pax_global_header 0000666 0000000 0000000 00000000064 14064067347 0014525 g ustar 00root root 0000000 0000000 52 comment=b802f7a55a644321db464655f879d036cbe39704 intranet-timesheet2-workflow-master/ 0000775 0000000 0000000 00000000000 14064067347 0020125 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/README.md 0000664 0000000 0000000 00000026341 14064067347 0021412 0 ustar 00root root 0000000 0000000 # ]po[ Timesheet Workflow This package is part of ]project-open[, an open-source enterprise project management system. For more information about ]project-open[ please see: * [Documentation Wiki](https://www.project-open.com/en/) * [V5.0 Download](https://sourceforge.net/projects/project-open/files/project-open/V5.0/) * [Installation Instructions](https://www.project-open.com/en/list-installers) About ]po[ Timesheet Workflow:
Installing this optional package adds functionality to ]po[ that allows supervisors to approve the hours logged by their reports using a simple but structured workflow.
# Online Reference Documentation ## Procedure Files
tcl/intranet-timesheet2-workflow-procs.tcl | Definitions for the intranet timesheet workflow |
eval_wf_start_date | Helper routine to evaluate start for each week in TS calendar view for Weekly TS confirmation | |
im_timesheet2_workflow_unsubmitted_hours_user_notification_sweeper | Sweeper process (executed by "cron" type of function, so it doesn't have access to ad_form and other functions) that checks if users have logged hours that are not yet submitted to a timesheet workflow. | |
im_timesheet_conf_new_page_wf_perm_delete_button | Should we show the "Delete" button in the TimesheetConfNewPage? The button is visible only for the Owner of the timesheet and the Admin, but nobody else during the course of the WF. | |
im_timesheet_conf_new_page_wf_perm_edit_button | Should we show the "Edit" button in the TimesheetConfNewPage? | |
im_timesheet_conf_new_page_wf_perm_table | Returns a hash array representing (role x status) -> (v r d w a), controlling the read and write permissions on the Timesheet Conf Object's new page, depending on the users's role and the WF status. | |
im_timesheet_conf_obj_status_active | ||
im_timesheet_conf_obj_status_deleted | ||
im_timesheet_conf_obj_status_rejected | ||
im_timesheet_conf_obj_status_requested | ||
im_timesheet_conf_obj_type_default | ||
im_timesheet_conf_object_delete | Delete a confirmation object for the specified (main-) project that covers the specified day. | |
im_timesheet_conf_object_new | Create a new confirmation object | |
im_timesheet_conf_object_notify_supervisor | Notifiy supervisor if hours are touched in the past. | |
im_timesheet_workflow_spawn_update_workflow | Check if there is already a WF running for that project/user/date and either reset this WF or create a new one if there wasn't one before. |
www/ | |
absences/ | |
absence-panel.adp | |
absence-panel.tcl | |
admin/ | |
clean-up-conf-objects.tcl | Admin page for intranet-timesheet2-workflow |
index.adp | |
index.tcl | Admin page for intranet-timesheet2-workflow |
conf-objects/ | |
conf-obj-panel.adp | |
conf-obj-panel.tcl | |
delete.tcl | Takes commands from the /intranet-notes/index page or the notes-list-compomponent and perform the selected action an all selected notes. |
index.adp | |
index.tcl | |
new-timesheet-workflow.adp | |
new-timesheet-workflow.tcl | Creates a new workflow for the associated hours |
new.adp | |
new.tcl | |
reports/ | |
unsubmitted-hours.tcl | Report listing all main projects in the system with all available fields + DynFields from projects and customers |
email=$email
system_owner=$system_owner
subject=$subject
message=$message
" } else { if {[catch { ns_sendmail $email $system_owner $subject $message } errmsg]} { ns_log Error "im_timesheet2_workflow_unsubmitted_hours_user_notification_sweeper: Error sending to \"$email\": $errmsg" } else { ns_log Notice "im_timesheet2_workflow_unsubmitted_hours_user_notification_sweeper: Sent mail to $email\n" } } } ns_log Notice "im_timesheet2_workflow_unsubmitted_hours_user_notification_sweeper: Finished" } ad_proc im_timesheet_conf_object_notify_supervisor { -modified_projects_tuples -user_id:required {-debug_p 0} } { Notifiy supervisor if hours are touched in the past. } { ns_log Notice "im_timesheet_conf_object_notify_supervisor: Starting" # Hash map project_id -> list of julian dates array set modified_projects_hash $modified_projects_tuples array set ansi_hash {} foreach pid [array names modified_projects_hash] { set julian_list $modified_projects_hash($pid) foreach j $julian_list { set ansi [im_date_julian_to_ansi $j] set ansi_hash($ansi) $ansi } } # Sorted unique list of dates where hours were modified set ansi_list [lsort -unique [array names ansi_hash]] # ToDo: # - Connect to notification mechanism set user_id $user_id set supervisor_email [db_string supervisor "select im_email_from_user_id(supervisor_id) from im_employees where employee_id = :user_id" -default ""] if {"" != $supervisor_email} { # Determine the sender address set sender_email [im_parameter -package_id [ad_acs_kernel_id] SystemOwner "" [ad_system_owner]] set user_name [db_string user_name "select im_name_from_user_id(:user_id) from dual" -default "unknown"] set subject [lang::message::lookup "" intranet-timesheet2-workflow.User_Modified_Past_Hours "User %user_name% Modified Past Hours"] set project_sql " select distinct main_p.project_name from im_projects sub_p, im_projects main_p where sub_p.project_id in ([join [array names modified_projects_hash] ","]) and main_p.tree_sortkey = tree_root_key(sub_p.tree_sortkey) " set project_list_txt "" db_foreach projects $project_sql { append project_list_txt "- $project_name\n" } set ansi_list_txt "" foreach ansi $ansi_list { append ansi_list_txt "- $ansi\n" } set message [lang::message::lookup "" intranet-timesheet2-workflow.User_Modified_Past_Hours_message " User %user_name% has modified hours in the following projects:\n %project_list_txt% \non the following dates:\n %ansi_list_txt% "] if {[catch { ns_sendmail $supervisor_email $sender_email $subject $message } errmsg]} { ns_log Error "im_timesheet_conf_object_delete: Error sending to \"$supervisor_email\": $errmsg" } else { ns_log Notice "im_timesheet_conf_object_delete: Sent mail to $email\n" } } } intranet-timesheet2-workflow-master/www/ 0000775 0000000 0000000 00000000000 14064067347 0020751 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/www/absences/ 0000775 0000000 0000000 00000000000 14064067347 0022534 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/www/absences/absence-panel.adp 0000664 0000000 0000000 00000000017 14064067347 0025715 0 ustar 00root root 0000000 0000000 @html;noquote@ intranet-timesheet2-workflow-master/www/absences/absence-panel.tcl 0000664 0000000 0000000 00000006634 14064067347 0025746 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/absence-panel.tcl # # Copyright (C) 2003 - 2009 ]project-open[ # # All rights reserved. Please check # https://www.project-open.com/license/ for details. # ----------------------------------------------------------- # Page Head # # There are two different heads, depending whether it's called # "standalone" (TCL-page) or as a Workflow Panel. # ----------------------------------------------------------- if {[info exists task]} { # Workflow-Panel Head: # This code is called when this page is embedded in a WF "Panel" set task_id $task(task_id) set case_id $task(case_id) # Return-URL Logic set return_url "" if {[info exists task(return_url)]} { set return_url $task(return_url) } set absence_id [db_string pid "select object_id from wf_cases where case_id = :case_id" -default ""] } else { # Stand-Alone Head: # This code is called when the page is used as a normal "EditPage" or "NewPage". ad_page_contract { Purpose: form to add a new project or edit an existing one } { absence_id:integer { return_url "/intranet/" } { task_id "" } } # Get the task_id if we've got the project if {"" == $task_id} { set case_id [db_string case_id "select case_id from wf_cases where object_id = :absence_id" -default 0] set tasks [db_list tasks "select task_id from wf_tasks where case_id=:case_id and state in ('started', 'enabled')"] switch [llength $tasks] { 0 { ad_return_complaint 1 "Didn't find task for project \#$project_id" } 1 { set task_id [lindex $tasks 0] } default { # Multiple tasks found. # Take just any one assigned to the user, because the user needs to do # all of them anyway. foreach tid $tasks { array set task_info [wf_task_info $tid] if {$task_info(this_user_is_assigned_p)} { set task_id $tid} } if {"" == $task_id} { ad_return_complaint 1 "You are not assigned to any task in project \#$project_id" } } } } if {[catch { array set task [wf_task_info $task_id] } err_msg]} { ad_return_complaint 1 "
Dieser Fehler kann auftreten, wenn ein Administrator einen RFC 'hart' gelöscht hat. Im normalen Betrieb sollte das nicht passieren.
Bitte kontaktieren Sie Ihren System Administrator.
"
return
}
set page_title [lang::message::lookup "" intranet-timesheet2-workflow.Absence "Absence"]
set context_bar [im_context_bar [list /intranet-rfc/projects/ "[lang::message::lookup "" intranet-timesheet2-workflow.Absences "Absences"]"] $page_title]
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
set transition_key [db_string transition_key "select transition_key from wf_tasks where task_id = :task_id" -default ""]
set current_user_id [auth::require_login]
set object_name [db_string name "select acs_object__name(:absence_id)"]
# ---------------------------------------------------------------
# Get the included hours
# ---------------------------------------------------------------
set params [list \
[list absence_id $absence_id] \
[list return_url $return_url] \
[list enable_master_p 0] \
[list form_mode display] \
[list panel_p 1] \
]
set html [ad_parse_template -params $params "/packages/intranet-timesheet2/www/absences/new"]
intranet-timesheet2-workflow-master/www/admin/ 0000775 0000000 0000000 00000000000 14064067347 0022041 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/www/admin/clean-up-conf-objects.tcl 0000775 0000000 0000000 00000002355 14064067347 0026633 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/admin/clean-up-conf-objects.tcl
#
# Copyright (C) 2003-2016 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/en/project-open-license for details.
ad_page_contract {
Admin page for intranet-timesheet2-workflow
@author klaus.hofeditz@project-open.com
} {
{ return_url "/intranet-timesheet2-workflow/admin/" }
}
set user_id [auth::require_login]
set page_title [lang::message::lookup "" intranet-timesheet2-workflow.CleanupConfigurationObjects "Clean up configuration objects"]
if { [im_is_user_site_wide_or_intranet_admin $user_id] } {
# set im_hours::conf_object_id to NULL in case there's no WF case
db_dml set_im_hours_conf_object_id "update im_hours set conf_object_id = null where conf_object_id not in ( select object_id from wf_cases )"
# remove from im_timesheet_conf_objects if no wf_case
db_dml remove_from_im_timesheet_conf_objects "delete from im_timesheet_conf_objects where conf_id not in ( select distinct object_id from wf_cases )"
util_user_message -message "Config Objects have been cleaned up."
ad_returnredirect $return_url
} else {
ad_return_complaint xx "You are not allowed to perform this action. Please contact your SysAdmin"
}
intranet-timesheet2-workflow-master/www/admin/index.adp 0000775 0000000 0000000 00000001232 14064067347 0023637 0 ustar 00root root 0000000 0000000 This site is not linked, so we assume you know what you do. Otherwise do not use any of the links below. Cleans up 'configuration objects' in case WF cases have been deleted from the system.
Dieser Fehler kann auftreten, wenn ein Administrator einen RFC
'hart' gelöscht hat. Im normalen Betrieb sollte das nicht
passieren.
Bitte kontaktieren Sie Ihren System Administrator.
"
return
}
set page_title [lang::message::lookup "" intranet-cust-baselkb.Edit_RFC "Edit RFC"]
set context_bar [im_context_bar [list /intranet-rfc/projects/ "[lang::message::lookup "" intranet-cust-baselkb.RFCs "RFCs"]"] $page_title]
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
set transition_key [db_string transition_key "select transition_key from wf_tasks where task_id = :task_id" -default ""]
set current_user_id [auth::require_login]
set object_name [db_string name "select acs_object__name(:conf_id)" -default ""]
# ---------------------------------------------------------------
# Get the included hours
# ---------------------------------------------------------------
set params [list \
[list conf_id $conf_id] \
[list return_url $return_url] \
[list enable_master_p 0] \
[list form_mode display] \
[list panel_p 1] \
]
set html [ad_parse_template -params $params "/packages/intranet-timesheet2-workflow/www/conf-objects/new"]
intranet-timesheet2-workflow-master/www/conf-objects/delete.tcl 0000775 0000000 0000000 00000002436 14064067347 0025303 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/conf-objects/delete.tcl
#
# Copyright (C) 2003-2013 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/license/ for details.
ad_page_contract {
Takes commands from the /intranet-notes/index page or
the notes-list-compomponent and perform the selected
action an all selected notes.
@author frank.bergmann@project-open.com
} {
conf_id:multiple,optional
return_url
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
set user_id [auth::require_login]
if {![info exists conf_id] || 0 == [llength $conf_id]} {
aad_returnredirect $return_url
}
foreach cid $conf_id {
set project_id [db_string pid "select conf_project_id from im_timesheet_conf_objects where conf_id = :cid" -default ""]
im_project_permissions $user_id $project_id view_p read_p write_p admin_p
if {!$admin_p} {
ad_return_complaint 1 "[lang::message::lookup "" intranet-timesheet2-workflow.Insufficient_permission "Insufficient permissions"]"
ad_script_abort
} else {
db_string del_conf_object "select im_timesheet_conf_object__delete(:cid) from dual" -default ""
}
}
ad_returnredirect $return_url
intranet-timesheet2-workflow-master/www/conf-objects/index.adp 0000664 0000000 0000000 00000002100 14064067347 0025113 0 ustar 00root root 0000000 0000000
<%= [lang::message::lookup "" intranet-timesheet2-workflow.Creating_workflow_for_all_hours_logged "Creating workflows for all hours logged between %start_date% - %end_date%."] %>
Admin
intranet-timesheet2-workflow-master/www/admin/index.tcl 0000775 0000000 0000000 00000001142 14064067347 0023655 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/admin/index.tcl
#
# Copyright (C) 2003-2016 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/en/project-open-license for details.
ad_page_contract {
Admin page for intranet-timesheet2-workflow
@author klaus.hofeditz@project-open.com
} {
}
set user_id [auth::require_login]
set page_title [lang::message::lookup "" intranet-timesheet2-workflow.AdminHome "Admin TS Confirmation WF"]
if { ![im_is_user_site_wide_or_intranet_admin $user_id] } { ad_return_complaint xx "You need to be a SysAdmin to access this page." }
intranet-timesheet2-workflow-master/www/conf-objects/ 0000775 0000000 0000000 00000000000 14064067347 0023325 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/www/conf-objects/conf-obj-panel.adp 0000664 0000000 0000000 00000000017 14064067347 0026603 0 ustar 00root root 0000000 0000000 @html;noquote@
intranet-timesheet2-workflow-master/www/conf-objects/conf-obj-panel.tcl 0000664 0000000 0000000 00000006620 14064067347 0026627 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/conf-obj-panel.tcl
#
# Copyright (C) 2003 - 2009 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/license/ for details.
# -----------------------------------------------------------
# Page Head
#
# There are two different heads, depending whether it's called
# "standalone" (TCL-page) or as a Workflow Panel.
# -----------------------------------------------------------
if {[info exists task]} {
# Workflow-Panel Head:
# This code is called when this page is embedded in a WF "Panel"
set task_id $task(task_id)
set case_id $task(case_id)
# Return-URL Logic
set return_url ""
if {[info exists task(return_url)]} { set return_url $task(return_url) }
set conf_id [db_string pid "select object_id from wf_cases where case_id = :case_id" -default ""]
} else {
# Stand-Alone Head:
# This code is called when the page is used as a normal "EditPage" or "NewPage".
ad_page_contract {
Purpose: form to add a new project or edit an existing one
} {
conf_id:integer
{ return_url "/intranet/" }
{ task_id "" }
}
# Get the task_id if we've got the project
if {"" == $task_id} {
set case_id [db_string case_id "select case_id from wf_cases where object_id = :conf_id" -default 0]
set tasks [db_list tasks "select task_id from wf_tasks where case_id=:case_id and state in ('started', 'enabled')"]
switch [llength $tasks] {
0 { ad_return_complaint 1 "Didn't find task for project \#$project_id" }
1 {
set task_id [lindex $tasks 0]
}
default {
# Multiple tasks found.
# Take just any one assigned to the user, because the user needs to do
# all of them anyway.
foreach tid $tasks {
array set task_info [wf_task_info $tid]
if {$task_info(this_user_is_assigned_p)} { set task_id $tid}
}
if {"" == $task_id} {
ad_return_complaint 1 "You are not assigned to any task in project \#$project_id"
}
}
}
}
if {[catch {
array set task [wf_task_info $task_id]
} err_msg]} {
ad_return_complaint 1 "
intranet-timesheet2-workflow-master/www/conf-objects/index.tcl 0000664 0000000 0000000 00000022563 14064067347 0025150 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/index.tcl
#
# Copyright (C) 2003 - 2009 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/license/ for details.
# ---------------------------------------------------------------
# Page Contract
# ---------------------------------------------------------------
ad_page_contract {
@author frank.bergmann@project-open.com
} {
{ object_id:integer 0}
{ filter_owner_id:integer ""}
{ filter_status_id:integer ""}
{ filter_type_id:integer ""}
{ filter_cost_center_id:integer ""}
{ filter_start_date ""}
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
# User id already verified by filters
set user_id [auth::require_login]
set current_user_id $user_id
set page_focus "im_header_form.keywords"
set user_admin_p [im_is_user_site_wide_or_intranet_admin $current_user_id]
set date_format "YYYY-MM-DD"
set object_name [db_string object_name "select acs_object__name(:object_id)" -default [lang::message::lookup "" intranet-core.Unassigned "Unassigned"]]
set page_title [_ intranet-timesheet2-workflow.Timesheet_Approval]
set context_bar [im_context_bar $page_title]
set return_url [im_url_with_query]
set current_url [ns_conn url]
set edit_project_all_p [im_permission $current_user_id "edit_projects_all"]
if {![im_permission $user_id "view_timesheet_conf_all"]} {
set filter_owner_id $user_id
}
if {"" eq $filter_start_date} {
set filter_start_date [db_string one_year "select (now() - '1 year'::interval)::date"]
}
if {[catch {
if { $filter_start_date != [clock format [clock scan $filter_start_date] -format %Y-%m-%d] } {
ad_return_complaint 1 "[_ intranet-core.Start_Date] [lang::message::lookup "" intranet-core.IsNotaValidDate "is not a valid date"].
[lang::message::lookup "" intranet-core.Current_Value "Current value"]: '$filter_start_date'
"
}
} err_msg]} {
ad_return_complaint 1 "[_ intranet-core.Start_Date] [lang::message::lookup "" intranet-core.DoesNotHaveRightFormat "doesn't have the right format"].
[lang::message::lookup "" intranet-core.Current_Value "Current value"]: '$filter_start_date'
[lang::message::lookup "" intranet-core.Expected_Format "Expected Format"]: 'YYYY-MM-DD'"
}
# ---------------------------------------------------------------
# Admin Links
# ---------------------------------------------------------------
set admin_links ""
append admin_links " \n$admin_links
\n" }
set bulk_actions_list "[list]"
#[im_permission $user_id "delete_expense"]
set delete_expense_p 1
if {$delete_expense_p} {
lappend bulk_actions_list "[_ intranet-timesheet2-workflow.Delete]" "delete" "[_ intranet-timesheet2-workflow.Remove_checked_items]"
}
#[im_permission $user_id "add_expense_bundle"]
set create_invoice_p 1
if {$create_invoice_p} {
}
# ---------------------------------------------------------------
# Expenses info
# ---------------------------------------------------------------
# Variables of this page to pass through the expenses_page
set export_var_list [list]
# define list object
set list_id "confs_list"
template::list::create \
-name $list_id \
-multirow conf_lines \
-key conf_id \
-has_checkboxes \
-bulk_action_method GET \
-bulk_actions $bulk_actions_list \
-bulk_action_export_vars {
object_id
return_url
} \
-row_pretty_plural "[_ intranet-timesheet2-workflow.Confs_Items]" \
-elements {
conf_chk {
label ""
display_template {
@conf_lines.conf_chk;noquote@
}
}
period {
label "[lang::message::lookup {} intranet-timesheet2-workflow.Period Period]"
link_url_eval {[export_vars -base "/intranet-timesheet2-workflow/conf-objects/new" {conf_id {form_mode display}}]}
}
project_name {
label "[_ intranet-timesheet2-workflow.Project]"
link_url_eval {[export_vars -base "/intranet/projects/view" {{project_id $conf_project_id}}]}
}
conf_user_name {
label "[_ intranet-timesheet2-workflow.Conf_User]"
link_url_eval "/intranet/users/view?user_id=$conf_user_id"
}
conf_status {
label "[lang::message::lookup {} intranet-timesheet2-workflow.Conf_Status Status]"
}
}
# Only show conf_items of projects that are controlled by the user
set project_permission_where "1=1"
if {![im_permission $current_user_id "view_projects_all"]} {
set project_permission_where "
p.project_id in (
select r.object_id_one
from acs_rels r
where r.object_id_two = :current_user_id
)
"
}
if { $filter_status_id ne "" && $filter_status_id > 0 } {
lappend criteria "co.conf_status_id in ([join [im_sub_categories -include_disabled_p 1 $filter_status_id] ","])"
}
if { $filter_type_id ne "" && $filter_type_id != 0 } {
lappend criteria "co.conf_type_id in ([join [im_sub_categories -include_disabled_p 1 $filter_type_id] ","])"
}
if {0 != $filter_owner_id && "" != $filter_owner_id} {
lappend criteria "co.conf_user_id = :filter_owner_id"
}
if {"" != $filter_start_date} {
lappend criteria "co.start_date >= :filter_start_date::timestamptz"
}
if {"" != $filter_cost_center_id} {
set filter_cost_center_code [db_string cc_code "select cost_center_code from im_cost_centers where cost_center_id = :filter_cost_center_id" -default ""]
lappend criteria "co.conf_user_id in (
select pe.person_id
from im_cost_centers cc,
im_employees e,
persons pe
where substring(cc.cost_center_code for (length(:filter_cost_center_code))) = :filter_cost_center_code and
e.department_id = cc.cost_center_id and
e.employee_id = pe.person_id
)"
}
set where_clause [join $criteria " and\n "]
if { $where_clause ne "" } {
set where_clause " and $where_clause"
}
db_multirow -extend {conf_chk return_url period} conf_lines confs_lines "
select co.*,
p.project_name,
im_name_from_user_id(co.conf_user_id) as conf_user_name,
im_category_from_id(co.conf_status_id) as conf_status,
(select count(*)
from acs_rels r,
im_biz_object_members bom
where r.rel_id = bom.rel_id and
bom.object_role_id in (1301,1302,1303,1309) and
r.object_id_two = :current_user_id and
r.object_id_one = p.project_id
) as write_p
from im_timesheet_conf_objects co
LEFT OUTER JOIN im_projects p ON (co.conf_project_id = p.project_id)
where
$project_permission_where
$where_clause
" {
set return_url [im_url_with_query]
set conf_chk ""
if {!$edit_project_all_p && !$write_p} { set conf_chk "" }
set period "$start_date - $end_date"
}
# ---------------------------------------------------------------
# Filter with Dynamic Fields
# ---------------------------------------------------------------
set owner_options [util_memoize [list im_employee_options] 3600]
set cost_center_options [im_cost_center_options -include_empty 1]
set form_id "conf_filter"
set object_type "im_timesheet_conf_object"
set action_url "/intranet-timesheet2-workflow/conf-objects/index"
set form_mode "edit"
ad_form \
-name $form_id \
-action $action_url \
-mode $form_mode \
-method GET \
-export { order_by how_many view_name} \
-form {
{filter_start_date:text(text),optional {label "[_ intranet-timesheet2.Start_Date]"} {value "$filter_start_date"} {html {size 10}} {after_html {}}}
{filter_owner_id:text(select),optional {label "[lang::message::lookup {} intranet-confdb.Owner {Owner}]"} {options $owner_options }}
{filter_cost_center_id:text(select),optional {label "[lang::message::lookup {} intranet-confdb.Cost_Center {Cost Center}]"} {options $cost_center_options }}
{filter_status_id:text(im_category_tree),optional {label "[lang::message::lookup {} intranet-core.Conf_Item_Status {Status}]"} {custom {category_type "Intranet Conf Item Status" translate_p 1 package_key "intranet-confdb"}} }
}
im_dynfield::append_attributes_to_form \
-object_type $object_type \
-form_id $form_id \
-object_id 0 \
-advanced_filter_p 1
# Set the form values from the HTTP form variable frame
im_dynfield::set_form_values_from_http -form_id $form_id
im_dynfield::set_local_form_vars_from_http -form_id $form_id
array set extra_sql_array [im_dynfield::search_sql_criteria_from_form \
-form_id $form_id \
-object_type $object_type
]
# ---------------------------------------------------------------
#
# ---------------------------------------------------------------
eval [template::adp_compile -string {
@page_title@
@li_html;noquote@
intranet-timesheet2-workflow-master/www/conf-objects/new-timesheet-workflow.tcl 0000664 0000000 0000000 00000007306 14064067347 0030465 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/new-workflow.tcl
#
# Copyright (C) 2003-2008 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/license/ for details.
# ---------------------------------------------------------------
# Page Contract
# ---------------------------------------------------------------
ad_page_contract {
Creates a new workflow for the associated hours
@author frank.bergmann@project-open.com
} {
user_id
{ return_url "/intranet-timesheet2-workflow/conf-objects/index" }
{ start_date_julian "" }
{ end_date_julian "" }
{ workflow_key "" }
{ auth_token ""}
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
# By default we assume that the user will submit hours for himself
set wf_user_id $user_id
# Check for valid user/auth_token combination
set valid_p [im_valid_auto_login_p -check_user_requires_manual_login_p 0 -user_id $user_id -auto_login $auth_token]
if {!$valid_p} {
set user_id [auth::require_login]
}
set page_title "[lang::message::lookup "" intranet-timesheet2-workflow.Create_New_Timesheet_Workflow "New Timesheet Workflow(s)"]"
set context_bar [im_context_bar $page_title]
set page_focus "im_header_form.keywords"
set date_format_pretty "YYYY-MM-DD"
if { "" == $workflow_key } {
set workflow_key [parameter::get -package_id [apm_package_id_from_key intranet-timesheet2-workflow] -parameter "DefaultWorkflowKey" -default "timesheet_approval_wf"]
}
# ---------------------------------------------------------------
# Create new Timesheet Confirmation Objects and their WFs
# ---------------------------------------------------------------
# Get all hours for the current user in the last week and check
# whether the hours are logged on a task or on a project.
# If it's a task then search the parent_id hierarchy for the next
# "real" project.
set start_date [db_string start_date "select to_date(:start_date_julian, 'J')"]
set end_date [db_string start_date "select to_date(:end_date_julian, 'J')"]
set hours_sql "
select h.project_id,
p.project_type_id,
p.parent_id
from im_hours h,
im_projects p
where h.conf_object_id is null and
h.user_id = :wf_user_id and
h.day >= :start_date and
h.day <= :end_date and
h.project_id = p.project_id
"
set hours_list [list]
db_foreach hours $hours_sql {
# Go up the hierarchy only if there is a parent_id != null...
while {"" != $parent_id && $project_type_id == [im_project_type_task]} {
db_1row parent "
select project_id,
parent_id,
project_type_id
from im_projects
where project_id = :parent_id
"
}
# Us a hash in order to eliminate duplicates
set project_list_hash($project_id) $project_id
}
set project_list [array names project_list_hash]
set li_html ""
foreach project_id $project_list {
set project_name [util_memoize [list db_string pname "select project_name from im_projects where project_id=$project_id" -default "Error"]]
append li_html "\n$debug_html\n
\n"
}
if {0 == [llength $project_list]} {
append li_html "
@page_title@
The object has probably been deleted by its owner recently."
ad_script_abort
}
}
set edit_perm_func [parameter::get_from_package_key -package_key intranet-timesheet2-workflow -parameter TimesheetConfNewPageWfEditButtonPerm -default "im_timesheet_conf_new_page_wf_perm_edit_button"]
set delete_perm_func [parameter::get_from_package_key -package_key intranet-timesheet2 -parameter TimesheetConfNewPageWfDeleteButtonPerm -default "im_timesheet_conf_new_page_wf_perm_delete_button"]
if {[eval [list $edit_perm_func -conf_id $conf_id]]} {
lappend actions [list [lang::message::lookup {} intranet-timesheet2.Edit Edit] edit]
}
if {[eval [list $delete_perm_func -conf_id $conf_id]]} {
lappend actions [list [lang::message::lookup {} intranet-timesheet2.Delete Delete] delete]
}
}
# ------------------------------------------------------------------
# Delete pressed?
# ------------------------------------------------------------------
set button_pressed [template::form get_action form]
if {"delete" == $button_pressed} {
db_string conf_delete "select im_timesheet_conf_object__delete(:conf_id)"
# In some cases, return_url is set to the page showing the case that had been just deleted
if { [string first "/acs-workflow/case?case_id" [string tolower $return_url]] == 0 } {
ad_returnredirect "/intranet-timesheet2-workflow/conf-objects/"
} else {
ad_returnredirect $return_url
}
}
# ---------------------------------------------------------------
# The Form
# ---------------------------------------------------------------
set form_id "form"
ad_form \
-name $form_id \
-mode $form_mode \
-export "object_id return_url" \
-actions $actions \
-has_edit 1 \
-action "/intranet-timesheet2-workflow/conf-objects/new" \
-form {
conf_id:key
{conf_project_id:text(select) {label "[lang::message::lookup {} intranet-timesheet2-workflow.Conf_Project Project]"} {options $conf_project_options} {before_html $conf_project_nr} }
{conf_user_id:text(select) {label "[lang::message::lookup {} intranet-timesheet2-workflow.Conf_User User]"} {options $conf_user_options} }
{start_date:date(date),optional {label "[_ intranet-timesheet2.Start_Date]"} {}}
{end_date:date(date),optional {label "[_ intranet-timesheet2.End_Date]"} {}}
{conf_type_id:text(select) {label "[lang::message::lookup {} intranet-timesheet2-workflow.Conf_Type Type]"} {options $conf_type_options} }
{conf_status_id:text(select) {label "[lang::message::lookup {} intranet-timesheet2-workflow.Conf_Status Status]"} {options $conf_status_options} }
}
ad_form -extend -name $form_id \
-select_query {
select *
from im_timesheet_conf_objects
where conf_id = :conf_id
} -new_data {
db_exec_plsql create_conf "
SELECT im_conf__new(
:conf_id,
'im_conf',
now(),
:user_id,
'[ad_conn peeraddr]',
null,
:conf,
:object_id,
:conf_type_id,
[im_conf_status_active]
)
"
} -edit_data {
db_dml edit_conf "
update im_confs
set conf = :conf
where conf_id = :conf_id
"
} -after_submit {
ad_returnredirect $return_url
ad_script_abort
}
# ---------------------------------------------------------------
# Show comments made during the approval process
# ---------------------------------------------------------------
set show_comment_p [parameter::get -package_id [apm_package_id_from_key intranet-timesheet2-workflow] -parameter "ShowCommentsInPanel" -default 1]
if { $show_comment_p } {
set comment [db_string get_comment "select comment from im_timesheet_conf_objects where conf_id = :conf_id" -default 0]
}
# ---------------------------------------------------------------
# Format the link to modify hours
# ---------------------------------------------------------------
set modify_hours_link ""
if {[info exists conf_id]} {
set conf_user_id [db_string conf_user "select conf_user_id from im_timesheet_conf_objects where conf_id = :conf_id" -default 0]
if {$conf_user_id == $user_id} {
set julian_date [db_string ts_date "
select to_char(co.start_date, 'J')
from im_timesheet_conf_objects co
where conf_id = :conf_id
" -default ""]
set modify_hours_msg [lang::message::lookup "" intranet-timesheet2-workflow.Modify_Included_Hours "Modify Included Hours"]
set modify_hours_url [export_vars -base "/intranet-timesheet2/hours/new" {julian_date {show_week_p 1}}]
set modify_hours_link "$modify_hours_msg"
set modify_hours_link "\n
\n"
}
}
# ---------------------------------------------------------------
# Show the included hours
# ---------------------------------------------------------------
set included_hours_msg [lang::message::lookup "" intranet-timesheet2-workflow.Included_Hours "Included Hours"]
set export_var_list [list]
set bulk_actions_list [list]
set list_id "included_hours"
template::list::create \
-name $list_id \
-multirow multirow \
-key conf_id \
-has_checkboxes \
-bulk_actions $bulk_actions_list \
-bulk_action_export_vars { object_id } \
-row_pretty_plural "[_ intranet-timesheet2-workflow.Included_Hours]" \
-elements {
date_pretty {
label "[lang::message::lookup {} intranet-timesheet2-workflow.Date Date]"
}
project_name {
label "[_ intranet-timesheet2.Project]"
link_url_eval {[export_vars -base "/intranet/projects/view" {project_id}]}
}
conf_user_name {
label "[_ intranet-timesheet2.User]"
link_url_eval "/intranet/users/view?user_id=$user_id"
}
hours {
label "[lang::message::lookup {} intranet-timesheet2-workflow.Hours Hours]"
}
note {
label "[lang::message::lookup {} intranet-timesheet2-workflow.Note Note]"
}
}
if {![info exists conf_id]} { ad_return_complaint 1 "Error: conf_id doesn't exist" }
# ad_return_complaint 1 $conf_id
db_multirow -extend {conf_chk return_url period} multirow multirow "
select
h.*,
co.*,
p.project_name,
im_name_from_user_id(co.conf_user_id) as conf_user_name,
to_char(h.day, 'YYYY-MM-DD') as date_pretty
from
im_hours h,
im_projects p,
im_timesheet_conf_objects co
where
h.project_id = p.project_id
and co.conf_id = :conf_id
and h.conf_object_id = co.conf_id
order by
h.day,
lower(p.project_name)
" {
set return_url [im_url_with_query]
set conf_chk ""
}
intranet-timesheet2-workflow-master/www/reports/ 0000775 0000000 0000000 00000000000 14064067347 0022447 5 ustar 00root root 0000000 0000000 intranet-timesheet2-workflow-master/www/reports/unsubmitted-hours.tcl 0000775 0000000 0000000 00000042233 14064067347 0026663 0 ustar 00root root 0000000 0000000 # /packages/intranet-timesheet2-workflow/www/unsubmitted-hours.tcl
#
# Copyright (C) 2003-2008 ]project-open[
#
# All rights reserved. Please check
# https://www.project-open.com/ for licensing details.
ad_page_contract {
Report listing all main projects in the system with all available
fields + DynFields from projects and customers
} {
{ level_of_detail 2 }
{ start_date "" }
{ end_date "" }
{ output_format "html" }
{ number_locale "" }
{ type_of_hours "" }
{ project_id:integer 0}
{ company_id:integer 0}
{ project_status_id:integer 0}
{ project_type_id:integer 0}
{ member_id:integer 0}
{ project_lead_id:integer 0}
}
# ------------------------------------------------------------
# Security
# Label: Provides the security context for this report
# because it identifies unquely the report's Menu and
# its permissions.
set menu_label "reporting-timesheet-unsubmitted-hours"
set current_user_id [auth::require_login]
set read_p [db_string report_perms "
select im_object_permission_p(m.menu_id, :current_user_id, 'read')
from im_menus m
where m.label = :menu_label
" -default "f"]
if {"t" != $read_p} {
ad_return_complaint 1 "
[lang::message::lookup "" intranet-reporting.You_dont_have_permissions "You don't have the necessary permissions to view this page"]"
ad_script_abort
}
# ------------------------------------------------------------
# Constants
set locale [lang::user::locale]
if {"" == $number_locale} { set number_locale $locale }
set date_format "YYYY-MM-DD"
set number_format "999,999.99"
# Check that Start & End-Date have correct format
if {"" != $start_date && ![regexp {[0-9][0-9][0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]} $start_date]} {
ad_return_complaint 1 "Start Date doesn't have the right format.
Current value: '$start_date'
Expected format: 'YYYY-MM-DD'"
}
if {"" != $end_date && ![regexp {[0-9][0-9][0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]} $end_date]} {
ad_return_complaint 1 "End Date doesn't have the right format.
Current value: '$end_date'
Expected format: 'YYYY-MM-DD'"
}
# Default is "unsubmitted hours" - hours not yet submitted to a TS workflow
if {"" == $type_of_hours} { set type_of_hours "unsubmitted" }
set page_title [lang::message::lookup "" intrant-timesheet2-workflow.Unsubmitted_Hours "Unsubmitted Hours"]
set context_bar [im_context_bar $page_title]
set context ""
set levels [list 1 [lang::message::lookup "" intranet-timesheet2-workflow.User_Only "User Only"] 2 [lang::message::lookup "" intranet-timesheet2-workflow.User_and_Month "User + Month"] 3 [lang::message::lookup "" intranet-timesheet2-workflow.All_Details "All Details"]]
set type_of_hours_options [list \
"all" [lang::message::lookup "" intranet-timesheet2-workflow.All_Hours_option "All Hours"] \
"unsubmitted" [lang::message::lookup "" intranet-timesheet2-workflow.Unsubmitted_Hours_option "Unsubmitted Hours"] \
"submitted" [lang::message::lookup "" intranet-timesheet2-workflow.Submitted_Hours_option "Submitted Hours"] \
"unapproved" [lang::message::lookup "" intranet-timesheet2-workflow.Unapproved_Hours_option "Unapproved Hours"] \
"approved" [lang::message::lookup "" intranet-timesheet2-workflow.Approved_Hours_option "Approved Hours"] \
"requested" [lang::message::lookup "" intranet-timesheet2-workflow.Requested_Hours_option "Requested Hours"] \
]
if {"" == $start_date} { set start_date "2000-01-01" }
if {"" == $end_date} { set end_date "2099-12-31" }
# Maxlevel is 4. Normalize in order to show the right drop-down element
if {$level_of_detail > 3} { set level_of_detail 3 }
# ------------------------------------------------------------
# Permissions - Unprivileged users can only see their own hours
#
set view_hours_all_p [expr {[im_permission $current_user_id view_hours_all] || [im_permission $current_user_id add_hours_all]}]
set view_hours_direct_reports_p [im_permission $current_user_id add_hours_direct_reports]
if {!$view_hours_all_p && !$view_hours_direct_reports_p} { set member_id $current_user_id }
if {!$view_hours_all_p && $view_hours_direct_reports_p} {
# Only see direct reports
set direct_reports [im_user_direct_reports_ids -user_id $current_user_id]
if {"" != $member_id} {
# A specific user was set - ccheck if that's allowed
if {[lsearch $direct_reports $member_id] < 0} {
# Bad member_id - attempted security breach
set member_id $current_user_id
}
} else {
# No specific member was set, but !!!
}
}
if {!$view_hours_all_p && !$view_hours_direct_reports_p} {
set member_id $current_user_id
}
# ------------------------------------------------------------
# Determine the user drop-down box depending on permissions
# Unprivileged user
set member_options [list [list [im_name_from_user_id $current_user_id] $current_user_id]]
# User who can see his direct reports
if {$view_hours_direct_reports_p} {
set member_options [im_user_direct_reports_options -user_id $current_user_id]
}
# User can see all users
if {$view_hours_all_p} {
set member_options [im_user_options \
-include_empty_p 0 \
-group_id [im_profile_employees] \
]
}
set member_options [linsert $member_options 0 [list [_ intranet-core.--_Please_select_--] ""]]
# ------------------------------------------------------------
# URLs for report links
set current_url [im_url_with_query]
set company_url "/intranet/companies/view?company_id="
set project_url "/intranet/projects/view?project_id="
set conf_object_url "/intranet-timesheet2-workflow/conf-objects/new?form_mode=display&conf_id="
set user_url "/intranet/users/view?user_id="
set this_url [export_vars -base "/intranet-timesheet2-workflow/reports/unsubmitted-hours" {start_date end_date level_of_detail project_id project_lead_id} ]
set wf_approval_url [export_vars -base "/acs-workflow/task" {{return_url $current_url}}]
# BaseURL for drill-down. Needs company_id, project_id, user_id, level_of_detail
set base_url [export_vars -base "/intranet-timesheet2-workflow/reports/unsubmitted-hours" {start_date end_date}]
# ------------------------------------------------------------
# Conditional SQL Where-Clause
#
set criteria [list]
if {0 != $project_id && "" != $project_id} { lappend criteria "p.project_id = :project_id" }
if {0 != $project_status_id && "" != $project_status_id} { lappend criteria "p.project_status_id in ([join [im_sub_categories $project_status_id] ","])" }
if {0 != $project_type_id && "" != $project_type_id} { lappend criteria "p.project_type_id in ([join [im_sub_categories $project_type_id] ","])" }
if {0 != $project_lead_id && "" != $project_lead_id} { lappend criteria "main_p.project_lead_id = :project_lead_id" }
if {0 != $company_id && "" != $company_id} { lappend criteria "p.company_id = :company_id" }
if {0 != $member_id && "" != $member_id} { lappend criteria "h.user_id = :member_id" }
switch $type_of_hours {
"all" { }
"unsubmitted" { lappend criteria "tco.conf_id is null" }
"submitted" { lappend criteria "tco.conf_id is not null" }
"unapproved" { lappend criteria "tco.conf_status_id != [im_timesheet_conf_obj_status_active]" }
"approved" { lappend criteria "tco.conf_status_id = [im_timesheet_conf_obj_status_active]" }
"requested" { lappend criteria "tco.conf_status_id = [im_timesheet_conf_obj_status_requested]" }
default { }
}
set where_clause [join $criteria " and\n\t\t"]
if { $where_clause ne "" } {
set where_clause " and $where_clause"
}
# ------------------------------------------------------------
# List of project variables to show
set hours_monthly_sum_counter [list \
pretty_name "Hours Monthly" \
var hours_monthly_sum \
reset \$user_day_month \
expr "\$hours+0" \
]
set hours_subtotal_counter [list \
pretty_name "Hours Subtotal" \
var hours_subtotal \
reset \$user_id \
expr "\$hours+0" \
]
set counters [list \
$hours_monthly_sum_counter \
$hours_subtotal_counter \
]
# ------------------------------------------------------------
# Define the report - SQL, counters, headers and footers
#
set sql "
select *,
to_char(day, :date_format) as day_pretty,
to_char(day, 'YYYY-MM') as day_month,
im_category_from_id(conf_status_id) as conf_status,
im_name_from_user_id(user_id) as user_name,
im_name_from_user_id(main_project_lead_id) as main_project_lead_name,
user_id || '00' || to_char(day, 'YYYYMM') as user_day_month,
(
select wtr.transition_name
from wf_tasks wta,
wf_transitions wtr
where wta.task_id = t.task_id and
wtr.workflow_key = wta.workflow_key and
wtr.transition_key = wta.transition_key
) as transition_name
from (
select
h.day,
h.user_id,
main_p.project_id as main_project_id,
main_p.project_name as main_project_name,
main_p.project_lead_id as main_project_lead_id,
c.company_id,
c.company_name,
tco.conf_id,
tco.conf_status_id,
wt.task_id,
sum(h.hours) as hours
from
im_hours h
LEFT OUTER JOIN im_timesheet_conf_objects tco ON (h.conf_object_id = tco.conf_id)
LEFT OUTER JOIN wf_cases wc ON (wc.object_id = tco.conf_id)
LEFT OUTER JOIN wf_tasks wt ON (wt.case_id = wc.case_id and wt.state != 'finished'),
im_projects p,
im_projects main_p,
im_companies c
where
h.project_id = p.project_id and
tree_root_key(p.tree_sortkey) = main_p.tree_sortkey and
main_p.company_id = c.company_id
and main_p.project_status_id not in ([im_project_status_deleted])
and h.day >= to_timestamp(:start_date, 'YYYY-MM-DD')
and h.day < to_timestamp(:end_date, 'YYYY-MM-DD')
$where_clause
group by
h.day,
h.user_id,
main_p.project_id,
main_p.project_name,
main_p.project_lead_id,
c.company_id,
c.company_name,
tco.conf_id,
tco.conf_status_id,
wt.task_id
) t
order by
user_name,
day_month,
company_name,
main_project_name,
day
"
# Global header/footer
set conf_object_l10n [lang::message::lookup "" intranet-timesheet2-workflow.Conf_Object "Conf Object"]
set header0 [list [_ intranet-core.User] [_ intranet-core.Month] [_ intranet-core.Customer] [_ intranet-core.Project_Name] [_ intranet-core.Project_Manager] [_ intranet-core.Date] $conf_object_l10n [_ intranet-timesheet2.Hours] [_ intranet-core.Workflow] ]
set footer0 {}
set report_def [list \
group_by user_id \
header {
"\#colspan=99 $user_name"
} \
content [list \
group_by day_month \
header {
""
"#colspan=98 $day_month"
} \
content [list \
header {
""
"$day_month"
"$company_name"
"$main_project_name"
"$main_project_lead_name"
"$day_pretty"
"$conf_status"
"#align=right $hours_pretty"
"$transition_name_l10n"
} \
content [list] \
]\
footer {"" "" "" "" "" "" "" "#align=right $hours_monthly_sum_pretty" ""} \
] \
footer {"" "" "" "" "" "" "" "#align=right $hours_subtotal_pretty" ""} \
]
# ------------------------------------------------------------
# Start Formatting the HTML Page Contents
# Write out HTTP header, considering CSV/MS-Excel formatting
im_report_write_http_headers -output_format $output_format -report_name "unsubmitted-hours"
switch $output_format {
html {
ns_write "
[im_header $page_title]
[im_navbar reporting]
\n"
}
}
im_report_render_row \
-output_format $output_format \
-row $header0 \
-row_class "rowtitle" \
-cell_class "rowtitle"
set footer_array_list [list]
set last_value_list [list]
set class "rowodd"
db_foreach sql $sql {
set hours_pretty [im_report_format_number $hours $output_format $number_locale]
set conf_object_name $conf_object_l10n
if {"" == $conf_id} { set conf_object_name "" }
switch [string tolower $conf_status] {
"" {
# No configuration object yet - unsubmitted hours
set color "red"
}
active {
# After confirmation by supervisor
set color "#20A003"
}
rejected {
# "rejected" means "before the confirmation process"
# The stauts name is misleading
set color "#F77809"
set conf_status "Not Confirmed"
}
requested {
# Submitted for approval, but not approved yet
set color "#F77809"
}
default {
set color "black"
}
}
# Format the "Approve" button behind WF controlled hours
set transition_name_l10n $transition_name
regsub -all " " $transition_name "_" transition_name_subs
if {"" ne $transition_name_subs} {
set transition_name_l10n [lang::message::lookup "" intranet-workflow.$transition_name_subs $transition_name]
}
set wf_action_button_class "button"
if {"" == $transition_name} {
set wf_action_button_class ""
set transition_name_l10n ""
}
im_report_display_footer \
-output_format $output_format \
-group_def $report_def \
-footer_array_list $footer_array_list \
-last_value_array_list $last_value_list \
-level_of_detail $level_of_detail \
-row_class $class \
-cell_class $class
im_report_update_counters -counters $counters
set hours_monthly_sum [expr {round(100.0 * $hours_monthly_sum) / 100.0}]
set hours_subtotal [expr {round(100.0 * $hours_subtotal) / 100.0}]
set hours_subtotal_pretty [im_report_format_number $hours_subtotal $output_format $number_locale]
set hours_monthly_sum_pretty [im_report_format_number $hours_monthly_sum $output_format $number_locale]
set last_value_list [im_report_render_header \
-output_format $output_format \
-group_def $report_def \
-last_value_array_list $last_value_list \
-level_of_detail $level_of_detail \
-row_class $class \
-cell_class $class
]
set footer_array_list [im_report_render_footer \
-output_format $output_format \
-group_def $report_def \
-last_value_array_list $last_value_list \
-level_of_detail $level_of_detail \
-row_class $class \
-cell_class $class
]
}
im_report_display_footer \
-output_format $output_format \
-group_def $report_def \
-footer_array_list $footer_array_list \
-last_value_array_list $last_value_list \
-level_of_detail $level_of_detail \
-display_all_footers_p 1 \
-row_class $class \
-cell_class $class
im_report_render_row \
-output_format $output_format \
-row $footer0 \
-row_class $class \
-cell_class $class
# Write out the HTMl to close the main report table
# and write out the page footer.
#
switch $output_format {
html { ns_write "
\n[im_footer]\n"}
}