Prerequisites:

  • Make sure that user who runs nsd has write permissions of folder the PID is written to.
  • Existence of /etc/init.d/aolserver4-projop (see Ubuntu installer script on http://www.project-open.org)

a) Install monit
b) Adjust /etc/monit/monitrc
c) Create /etc/monit/conf.d/aolserver.monitrc

check process aolserver4-projop with pidfile /usr/lib/aolserver4/log/nspid.projop
      start program "/etc/init.d/aolserver4-projop start"
      stop program "/etc/init.d/aolserver4-projop stop"

d) Adjust /etc/default/monit:

startup=1

e) start

sudo /etc/init.d/monit start

Installation von PostgreSQL 8.2 from source:

psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket “/var/run/postgresql/.s.PGSQL.5432″?

http://askubuntu.com/questions/50621/cannot-connect-to-postgresql-on-port-5432

I would guess that the server is actually listening on the socket /tmp/.s.PGSQL.5432 rather than the /var/run/postgresql/.s.PGSQL.5432 that your client is attempting to connect to. This is a typical problem when using hand-compiled or third-party PostgreSQL packages on Debian or Ubuntu, because the source default for the Unix-domain socket directory is /tmp but the Debian packaging changes it to /var/run/postgresql.

Configure internet access (Minimal install VM images)

Setting WM Player

Set Network Connection of VM to: Bridged

In some cases you need to set the keyboard:

loadkeys de/es/fr ...

If you copied a VM you need to delete:

/etc/udev/rules.d/70-persistent-net.rules

and reboot:

reboot

Check if

ls /sys/class/net

list now eth0

Settings in VM

/etc/sysconfig/network
NETWORKING=yes
HOSTNAME=localhost.localdomain
GATEWAY=192.168.1.1

/etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE="eth0"
NM_CONTROLLED="yes"
ONBOOT="yes"
BOOTPROTO="static"
IPADDR="192.168.1.39"
NETMASK="255.255.255.0"

/etc/resolv.conf
nameserver 192.168.1.1

Open Port 22:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

add /etc/init/aolserver.conf

# AOLServer

start on started sshd
stop on runlevel [016]

respawn
exec su -c "/usr/local/aolserver451/bin/nsd-postgres  -it /web/projop/etc/config.tcl" projop
initctl reload-configuration
initctl start aolserver

tcl: Continue line using backslash

1) Make sure there’s no backspace after the backslash !
2) No breaks allowed within function call

This won’t work:

                [im_do_row [array get bgcolor] $ctr $curr_owner_id \
                                $owner_name \
                                $days \
                                [array get user_days] \
                                [array get user_absences] \
                                $holydays \
                                $today_date \
                                [array get user_ab_descr] \
                                $workflow_key \
                           ]

OpenACS: Watch packages

  • AOLServer restart sets back watches
  • CVS settings (watch) could lead to additional dirs Base/Baseserv containing a second version of lib files

pgsql

# select project_id from im_projects where parent_id = NULL;
 project_id
------------
(0 rows)

# select project_id from im_projects where parent_id IS NULL;
 project_id
------------
      35116
      35417
(2 rows)


Location AOLServer:
./usr/local/aolserver451

Location tcl:
./usr/local/aolserver451/

----------------------------------------------------------------

export NS=/usr/local/aolserver451
export PG=/usr/lib/postgresql/8.2/

# Begin Compiling
echo Installing TCL 8.5.6
cd /usr/local/src/tcl8.5.6/unix
./configure --prefix=/usr/local/aolserver451/lib/tcl856 --with-encoding utf-8 --enable-threads --enable-symbols --enable-shared
make
make install
echo Installed TCL

echo Installing AOL Server
cd /usr/local/src/aolserver-4.5.1
./configure --prefix=/usr/local/aolserver451 --with-tcl=/usr/local/aolserver451/lib/tcl856/lib --enable-threads --enable-symbols
/usr/local/aolserver45/bin/tclsh8.4 nsconfig.tcl -debug -install /usr/local/aolserver451
make
make install
echo Installed AOL Server

echo Compiling other required modules
cd /usr/local/src/aolserver-4.5.1/nscache
make install NSHOME=/usr/local/aolserver451
echo Installed nscache

cd /usr/local/src/aolserver-4.5.1/nssha1
make install NSHOME=/usr/local/aolserver451
echo Installed nssha1

#cd /usr/local/src/aolserver-4.5.1/nsopenssl
#sudo make install OPENSSL=/usr/lib AOLSERVER=/usr/lib/aolserver4

cd /usr/local/src/aolserver-4.5.1/nspostgres
# make install POSTGRES=SEPARATELY PGINC=/usr/include/postgresql PGLIB=/var/lib/postgresql/8.2/main ACS=1 AOLSERVER=/usr/lib/aolserver4
# make install POSTGRES=SEPARATELY PGINC=/usr/include/postgresql PGLIB=/usr/lib/pgsql/ ACS=1 AOLSERVER=/usr/local/aolserver451
make install POSTGRES=SEPARATELY PGINC=/usr/local/pgsql/include/ PGLIB=/usr/local/src/aolserver-4.5.1/postgresql-8.2.19/src/interfaces/libpq ACS=1 AOLSERVER=/usr/local/aolserver451

make install POSTGRES=SEPARATELY PGINC=/usr/local/pgsql/include/ PGLIB=/usr/local/pgsql/lib ACS=1 AOLSERVER=/usr/local/aolserver451

cd /usr/local/src
tar xzvf tcllib-1.11.1.tar.gz
cd tcllib-1.11.1
./configure --prefix=/usr/local/aolserver451
make install
echo Installed TCL Lib

cd /usr/local/src/
tar xzvf xotcl-1.6.2.tar.gz
cd xotcl-1.6.2
./configure --enable-threads --enable-symbols --prefix=/usr/local/aolserver451 --exec-prefix=/usr/local/aolserver451 --with-tcl=/usr/local/aolserver451/lib/tcl856/lib
make
make install
echo Installed XOTcl

cd /usr/local/src/aolserver-4.5.1/tdom/unix
../configure --enable-threads --disable-tdomalloc --prefix=/usr/local/aolserver451 --exec-prefix=/usr/local/aolserver451 --with-aolserver=/usr/local/aolserver451 --with-tcl=/usr/local/aolserver451/lib/tcl856/lib
make install
echo Installed TDom

Aug 062011
################################################################
#
#   klaus.hofeditz@project-open.com
#   v0.1  - Last changed: 15/07/2011
#
###############################################################

TIMESTAMP=$(date +%Y%m%d%H%M%S)
/bin/cp /web/projop/log/error.log /web/projop/log/error.log.$TIMESTAMP
cat /web/projop/log/error.log | sed '1!d' > /web/projop/log/error.log

Crontab:

30 2 * * * sh /usr/local/etc/logroll.sh
Jun 092011

Messages

Ext.example.msg('Date Selected', 'You choose {0}.', Ext.Date.format(date, 'M j, Y'));

Sample REST Post

Get an object together with its attributes

        var clickHandlerAcceptQuote = function(project_id) {
                var xmlAsString = '380';
                Ext.Ajax.request({
                        url: '/intranet-rest/im_project/' + project_id + '?format=xml',
                        method: 'POST',
                        xmlData: xmlAsString,
                        success: function(o) {
                                if (o.responseText == 0) {
                                        field.markInvalid('Email already in use, please login');
                                }
                        }
                });
        };

Get a category

var ticketAreaStore = Ext.create('PO.data.CategoryStore', {
        storeId:        'ticketAreaStore',
        model: 'TicketBrowser.Category',
        remoteFilter:   true,
        autoLoad:       false,
        pageSize:       1000,
        proxy: {
                type: 'rest',
                url: '/intranet-rest/im_category',
                appendId: true,
                extraParams: {
                        format: 'json',
                        category_type: '\'Intranet Sencha Ticket Tracker Area\''
                },
                reader: { type: 'json', root: 'data' }
        }
});

Get a File Storage Object

// Local store definition.
// We have to redefine the store every time we show
// files for a different ticket
var fileStorageStore = Ext.create('Ext.data.Store', {
        model:  'TicketBrowser.FileStorage',
        storeId:        'fileStorageStore',
        autoLoad:       false,
        remoteSort:     true,
        pageSize:       5,                      // Enable pagination
        sorters:        [{
        property:       'name',
        direction:      'DESC'
        }],
        proxy:  {
        type:   'rest',
        url:    '/intranet-rest/file_storage_object',
        appendId:       true,
        extraParams:    { format: 'json', parent_id: 0 },
        reader: { type: 'json', root: 'data' }
        }
});

Conventions:

var naming:
Place holders (renderTo): Add ‘_ph’
Example: “grid_uploaded_files_ph”

Since its not sure yet if Dynfield support will be included in V4.0 of ]project-open[ following the instructions on how to patch your system to get the feature as shown in above video.

----------------------------------------------------------------------------------
a) Prepare
----------------------------------------------------------------------------------
- Please note that the patch requires some ]po[/OpenACS developer knowledge
- make sure you have installed the package “instranet-rest” is installed
- Download files

———————————————————————————-
b) Patch /web/projop/packages/intranet-dynfield/tcl/ intranet-dynfield-procs.tcl
———————————————————————————-

The following lines have been added:

Right on top of the function:

    upvar 1 ajax_post_data ajax_post_data_build
    set ajax_post_data ""

and right after “im_dynfield::append_attribute_to_form” add the following code

        if { 0 != $field_cnt } {
            append ajax_post_data_build " + "
        }
        if { "date" == $widget } {
            append ajax_post_data_build "\"<$attribute_name>\""
            append ajax_post_data_build " + evalReturnValue('date', "
            append ajax_post_data_build " document.invoices_dynfield\['$attribute_name.year'\].value + \"-\" + document.invoices_dynfield\['$attribute_name.month'\].value + \"-\" + document.invoices_dynfield\['$attribute_name.day'\].value, "
            append ajax_post_data_build "'$required_p'"
            append ajax_post_data_build ") "
            append ajax_post_data_build " + \"</$attribute_name>\""
 
        } else {
            append ajax_post_data_build "\"<$attribute_name>\" + evalReturnValue('text', document.invoices_dynfield.$attribute_name.value, '$required_p') + \"</$attribute_name>\""
        }

Following the patched function V3.5 (cvs branch 3-5-0-patches)

 
ad_proc -public im_dynfield::append_attributes_to_form {
    {-object_subtype_id "" }
    -object_type:required
    -form_id:required
    {-object_id ""}
    {-search_p "0"}
    {-form_display_mode "edit" }
    {-advanced_filter_p 0}
    {-include_also_hard_coded_p 0 }
    {-page_url "default" }
    {-debug 0}
} {
    Append intranet-dynfield attributes for object_type to an existing form.<p>
    @option object_type The object_type attributes you want to add to the form
    @option object_subtype_id Specifies the "subtype" of the objects (i.e. project_type_id)
    @option advanced_filter_p Tells us that the dynfields are used for an 
            "advanced filter" as oposed to a data form. Text fields dont make
            much sense here, so we'll skip them.
 
    @param include_also_hard_coded_p Should we include fields that are also hard
            coded in ]po[ screens?
 
    @param page_url
		Serves to identify the page layout.
 
    @return Returns the number of added fields
 
    The code consists of two main parts:
    <ul>
    <li>Adding the the attributes to the forum and
    <li>Extracting the values of the attributes from a number of storage tables.
    </ul>
} {
 
    upvar 1 ajax_post_data ajax_post_data_build
    set ajax_post_data ""
 
    if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: object_type=$object_type, object_id=$object_id" }
    set user_id [ad_get_user_id]
 
    # Does the specified layout page exist? Otherwise use "default".
    set page_url_exists_p [db_string exists "select count(*) from im_dynfield_layout_pages where object_type = :object_type and page_url = :page_url"]
    if {!$page_url_exists_p} { set page_url "default" }
    set form_page_url $page_url
 
    # Add a hidden "object_type" field to the form
    if {![template::element::exists $form_id "object_type"]} {
	if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: creating object_type=$object_type" }
    	template::element create $form_id "object_type" \
    			    -datatype text \
    			    -widget hidden \
    			    -value  $object_type
    }
 
    # add a hidden object_id field to the form
    if {[exists_and_not_null object_id]} {
    	if {![template::element::exists $form_id "object_id"]} {
	    if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: creating object_id=$object_id" }
	    template::element create $form_id "object_id" \
		-datatype integer \
		-widget hidden \
		-value  $object_id
    	}
    }
 
    # Get display mode per attribute and object_type_id
    set sql "
       select	m.attribute_id,
                m.object_type_id as ot,
                m.display_mode as dm
        from
                im_dynfield_type_attribute_map m,
                im_dynfield_attributes a,
                acs_attributes aa
        where
                m.attribute_id = a.attribute_id
                and a.acs_attribute_id = aa.attribute_id
                and aa.object_type = :object_type
    "
 
    # Default: Set all field to form's display mode
    set default_display_mode $form_display_mode
 
    db_foreach attribute_table_map $sql {
	set key "$attribute_id.$ot"
	set display_mode_hash($key) $dm
 
	# Now we've got atleast one display mode configured:
	# Set the default to "none", so that no field is shown
	# except for the configured fields.
	set default_display_mode "none"
 
	if {$debug} { ns_log Notice "append_attributes_to_form: display_mode($key) <= $dm" }
    }
 
    # Disable the mechanism if the object_type_id hasn't been specified
    # (compatibility mode)
 
 
 
    if {"" == $object_subtype_id} { set default_display_mode "edit" }
 
    db_1row object_type_info "
        select
                t.table_name as object_type_table_name,
                t.id_column as object_type_id_column
        from
                acs_object_types t
        where
                t.object_type = :object_type
    "
 
    set extra_wheres [list "1=1"]
    if {$advanced_filter_p} {
	lappend extra_wheres "aw.widget in (
		'select', 'generic_sql', 
		'im_category_tree', 'im_cost_center_tree',
		'checkbox'
	)"
    }
    set extra_where [join $extra_wheres "\n\t\tand "]
 
 
    # We need to exclude also_hard_coded_p fields for most forms
    # since a lot of meta-fields were added for the REST interface. 
    # Otherwise object creation will fail for most objects.
    set also_hard_coded_p_sql ""
    if {!$include_also_hard_coded_p} { 
	set also_hard_coded_p_sql "and (also_hard_coded_p is NULL or also_hard_coded_p = 'f')" 
    } 
 
    set attributes_sql "
	select *
	from (
		select
			dl.*,
			coalesce(dl.pos_y, 10000 + a.attribute_id) as pos_y_coalesce,
			a.attribute_id,
			aa.attribute_id as dynfield_attribute_id,
			a.table_name as attribute_table_name,
			tt.id_column as attribute_id_column,
			a.attribute_name,
			a.pretty_name,
			a.datatype, 
			case when a.min_n_values = 0 then 'f' else 't' end as required_p, 
			a.default_value, 
			aw.widget,
			aw.parameters,
			aw.storage_type_id,
			im_category_from_id(aw.storage_type_id) as storage_type
		from
			im_dynfield_attributes aa
			LEFT OUTER JOIN	(
				select	* 
				from	im_dynfield_layout 
				where	page_url = :form_page_url
			) dl ON (aa.attribute_id = dl.attribute_id),
			im_dynfield_widgets aw,
			acs_attributes a 
			left outer join 
				acs_object_type_tables tt 
				on (tt.object_type = :object_type and tt.table_name = a.table_name)
		where 
			a.object_type = :object_type
			and a.attribute_id = aa.acs_attribute_id
			and aa.widget_name = aw.widget_name
			and $extra_where
			$also_hard_coded_p_sql
		) t
	order by
		pos_y_coalesce
    "
 
    set field_cnt 0
    db_foreach attributes $attributes_sql {
 
	# Check if the elements as disabled in the layout page
	if {$page_url_exists_p && "" == $page_url} { continue }
 
	# Check if the current user has the right to read and write on the dynfield
	set read_p [im_object_permission \
			-object_id $dynfield_attribute_id \
			-user_id $user_id \
			-privilege "read" \
	]
	set write_p [im_object_permission \
			-object_id $dynfield_attribute_id \
			-user_id $user_id \
			-privilege "write" \
	]
	if {!$read_p} { continue }
 
	set display_mode $default_display_mode
 
	# object_subtype_id can be a list, so go through the list
	# and take the highest one (none - display - edit).
	foreach subtype_id $object_subtype_id {
	    set key "$dynfield_attribute_id.$subtype_id"
	    if {[info exists display_mode_hash($key)]} { 
		switch $display_mode_hash($key) {
		    edit { set display_mode "edit" }
		    display { if {$display_mode == "none"} { set display_mode "display" } }
		}
	    }
	}
 
	if {"edit" == $display_mode && "display" == $form_display_mode}  {
            set display_mode $form_display_mode
        }
	if {"edit" == $display_mode && !$write_p}  {
            set display_mode "display"
        }
 
	if {"none" == $display_mode} { continue }
 
	if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: attribute_name=$attribute_name, datatype=$datatype, widget=$widget, storage_type_id=$storage_type_id" }
 
	# set optional all attributes if search mode
	if {$search_p} { set required_p "f" }
 
	# No help yet...
	set help ""
 
	im_dynfield::append_attribute_to_form \
	    -attribute_name $attribute_name \
	    -widget $widget \
	    -form_id $form_id \
	    -datatype $datatype \
	    -display_mode $display_mode \
	    -parameters $parameters \
	    -required_p $required_p \
	    -pretty_name $pretty_name \
	    -help $help
 
        if { 0 != $field_cnt } {
            append ajax_post_data_build " + "
        }
        if { "date" == $widget } {
            append ajax_post_data_build "\"<$attribute_name>\""
            append ajax_post_data_build " + evalReturnValue('date', "
            append ajax_post_data_build " document.invoices_dynfield\['$attribute_name.year'\].value + \"-\" + document.invoices_dynfield\['$attribute_name.month'\].value + \"-\" + document.invoices_dynfield\['$attribute_name.day'\].value, "
            append ajax_post_data_build "'$required_p'"
            append ajax_post_data_build ") "
            append ajax_post_data_build " + \"</$attribute_name>\""
 
        } else {
            append ajax_post_data_build "\"<$attribute_name>\" + evalReturnValue('text', document.invoices_dynfield.$attribute_name.value, '$required_p') + \"</$attribute_name>\""
        }
 
	incr field_cnt
 
    }	
 
    # That's all until here IF this is a new object. Otherwise, we'll need 
    # to retreive the object's values from several tables and from the multi-fields...
    #
    if { ![template::form is_request $form_id] } { return }
    if { ![info exists object_id]} { return }
 
 
    # Same loop as before...
    db_foreach attributes $attributes_sql {
 
	# Check if the elements is disabled in the layout page
	if {$page_url_exists_p && "" == $page_url} { continue }
 
	# Check if the current user has the right to read the dynfield
	if {![im_object_permission -object_id $dynfield_attribute_id -user_id $user_id]} { continue }
 
	# Default display mode
	set display_mode $default_display_mode
 
	# object_subtype_id can be a list, so go through the list
	# and take the highest one (none - display - edit).
	foreach subtype_id $object_subtype_id {
	    set key "$dynfield_attribute_id.$subtype_id"
	    if {[info exists display_mode_hash($key)]} { 
		switch $display_mode_hash($key) {
		    edit { set display_mode "edit" }
		    display { if {$display_mode == "none"} { set display_mode "display" } }
		}
	    }
	}
 
#        set key "$dynfield_attribute_id.$object_subtype_id"
#        if {[info exists display_mode_hash($key)]} { set display_mode $display_mode_hash($key) }
 
	# Don't show "none" fields...
	if {"none" == $display_mode} { continue }
 
	switch $storage_type {
	    multimap {
 
		# "MultiMaps" (select with multiple values) are stored in a separate
		# "im_dynfield_attr_multi_value", because we can't store it like the
		# other attributes directly inside the object's table.
		if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: multipmap storage" }
		template::element set_properties $form_id $attribute_name "multiple_p" "1"
		set value_list [db_list get_multiple_values "
			select	value 
			from	im_dynfield_attr_multi_value
			where	attribute_id = :dynfield_attribute_id
				and object_id = :object_id
		"]
		template::element::set_values $form_id $attribute_name $value_list
 
	    }
 
	    date {
 
		# ToDo: Remove this part. It's not used anymore. Dates are stored as
		# values in YYYY-MM-DD format
		if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: date storage" }
		set value [template::util::date::get_property ansi [set $attribute_name]]
		set value_list [split $value "-"]			
		set value "[lindex $value_list 0] [lindex $value_list 1] [lindex $value_list 2]"
		template::element::set_value $form_id $attribute_name $value
 
	    }
 
	    value - default {
 
		# ToDo: slow. This piece issues N SQL statements, instead of constructing
		# a single SQL and issuing it once. Causes performance problems at BaselKB
		# for example.
		if {$debug} { ns_log Notice "im_dynfield::append_attributes_to_form: value - default storage" }
		set value [db_string get_single_value "
		    select	$attribute_name
		    from	$attribute_table_name
		    where	$attribute_id_column = :object_id
		" -default ""]
		template::element::set_value $form_id $attribute_name $value
 
	    }
	}
    }
    return $field_cnt
}

———————————————————————————-
d) Patch /web/projop/packages/intranet-invoices/www/view.tcl
———————————————————————————-

Relevant parts are the ones titled:

# —————————————————————————————-
# Check if there are Dynamic Fields of type date and localize them
# —————————————————————————————-

and

# ———————————————————————
# Dynfields
# ———————————————————————

Here’s the complete file /packages/intranet-invoices/www/view.tcl
based on V3.5 – cvs branch: b3-5-0-patches:

# /packages/intranet-invoices/www/view.tcl
#
# Copyright (C) 2003 - 2009 ]project-open[
#
# All rights reserved. Please check
# http://www.project-open.com/license/ for details.
 
ad_page_contract {
    View all the info about a specific project
 
    @param render_template_id specifies whether the invoice should be show
	   in plain HTML format or formatted using an .adp template
    @param show_all_comments whether to show all comments
    @param send_to_user_as "html" or "pdf".
           Indicates that the content of the
           invoice should be rendered using the default template
           and sent to the default contact.
           The difficulty is that it's not sufficient just to redirect
           to a mail sending page, because it is only this page that 
           "knows" how to render an invoice. So in order to send the
           PDF we first need to redirect to this page, render the invoice
           and then redirect to the mail sending page.
 
    @author frank.bergmann@project-open.com
} {
    { invoice_id:integer 0}
    { object_id:integer 0}
 
    { show_all_comments 0 }
    { render_template_id:integer 0 }
    { return_url "" }
    { send_to_user_as ""}
    { output_format "html" }
    { err_mess "" }
    { item_list_type:integer 0 }
}
 
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
 
# Get user parameters
set user_id [ad_maybe_redirect_for_registration]
set user_locale [lang::user::locale]
set locale $user_locale
set page_title ""
 
# Security is defered after getting the invoice information
# from the database, because the customer's users should
# be able to see this invoice even if they don't have any
# financial view permissions otherwise.
 
if {0 == $invoice_id} {set invoice_id $object_id}
if {0 == $invoice_id} {
    ad_return_complaint 1 "<li>[lang::message::lookup $locale intranet-invoices.lt_You_need_to_specify_a]"
    return
}
 
if {"" == $return_url} { set return_url [im_url_with_query] }
 
set bgcolor(0) "class=invoiceroweven"
set bgcolor(1) "class=invoicerowodd"
 
set required_field "<font color=red size=+1><B>*</B></font>"
 
# ---------------------------------------------------------------
# Set default values from parameters
# ---------------------------------------------------------------
 
# Number formats
set cur_format [im_l10n_sql_currency_format]
set vat_format $cur_format
set tax_format $cur_format
 
 
# Rounding precision can be between 2 (USD,EUR, ...) and -5 (Old Turkish Lira, ...).
set rounding_precision 2
set rounding_factor [expr exp(log(10) * $rounding_precision)]
set rf $rounding_factor
 
 
# Default Currency
set default_currency [ad_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
set invoice_currency [db_string cur "select currency from im_costs where cost_id = :invoice_id" -default $default_currency]
set rf 100
catch {set rf [db_string rf "select rounding_factor from currency_codes where iso = :invoice_currency" -default 100]}
 
# Show dynfields?
set show_dynfield_tab_p [ad_parameter -package_id [im_package_invoices_id] "DynamicFieldSupport" "" "0"]
 
# Where is the template found on the disk?
set invoice_template_base_path [ad_parameter -package_id [im_package_invoices_id] InvoiceTemplatePathUnix "" "/tmp/templates/"]
 
# Invoice Variants showing or not certain fields.
# Please see the parameters for description.
set discount_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceDiscountFieldP" "" 0]
set surcharge_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceSurchargeFieldP" "" 0]
set canned_note_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceCannedNoteP" "" 0]
set show_qty_rate_p [ad_parameter -package_id [im_package_invoices_id] "InvoiceQuantityUnitRateEnabledP" "" 0]
set show_our_project_nr [ad_parameter -package_id [im_package_invoices_id] "ShowInvoiceOurProjectNr" "" 1]
set show_our_project_nr_first_column_p [ad_parameter -package_id [im_package_invoices_id] "ShowInvoiceOurProjectNrFirstColumnP" "" 1]
set show_company_project_nr [ad_parameter -package_id [im_package_invoices_id] "ShowInvoiceCustomerProjectNr" "" 1]
set show_leading_invoice_item_nr [ad_parameter -package_id [im_package_invoices_id] "ShowLeadingInvoiceItemNr" "" 0]
 
# Show or not "our" and the "company" project nrs.
set company_project_nr_exists [im_column_exists im_projects company_project_nr]
set show_company_project_nr [expr $show_company_project_nr && $company_project_nr_exists]
 
 
# Which report to show for timesheet invoices as the detailed list of hours
set timesheet_report_url [ad_parameter -package_id [im_package_invoices_id] "TimesheetInvoiceReport" "" "/intranet-reporting/timesheet-invoice-hours.tcl"]
 
# Check if (one of) the PDF converter(s) is installed
set pdf_enabled_p [llength [info commands im_html2pdf]]
 
# Unified Business Language?
set ubl_enabled_p [llength [info commands im_ubl_invoice2xml]]
 
 
# ---------------------------------------------------------------
# Audit
# ---------------------------------------------------------------
 
# Check if the invoices was changed outside of ]po[...
# Normally, the current values of the invoice should match
# exactly the last registered audit version...
im_audit -object_id $invoice_id -action pre_update
 
 
# ---------------------------------------------------------------
# Determine if it's an Invoice or a Bill
# ---------------------------------------------------------------
 
set cost_type_id [db_string cost_type_id "select cost_type_id from im_costs where cost_id = :invoice_id" -default 0]
 
# Invoices and Quotes have a "Customer" fields.
set invoice_or_quote_p [expr $cost_type_id == [im_cost_type_invoice] || $cost_type_id == [im_cost_type_quote] || $cost_type_id == [im_cost_type_delivery_note] || $cost_type_id == [im_cost_type_interco_quote] || $cost_type_id == [im_cost_type_interco_invoice]]
 
# Vars for ADP (can't use the commands in ADP)
set quote_cost_type_id [im_cost_type_quote]
set delnote_cost_type_id [im_cost_type_delivery_note]
set po_cost_type_id [im_cost_type_po]
set invoice_cost_type_id [im_cost_type_invoice]
set bill_cost_type_id [im_cost_type_bill]
 
# Invoices and Bills have a "Payment Terms" field.
set invoice_or_bill_p [expr $cost_type_id == [im_cost_type_invoice] || $cost_type_id == [im_cost_type_bill]]
 
# CostType for "Generate Invoice from Quote" or "Generate Bill from PO"
set target_cost_type_id ""
set generation_blurb ""
if {$cost_type_id == [im_cost_type_quote]} {
    set target_cost_type_id [im_cost_type_invoice]
    set generation_blurb "[lang::message::lookup $locale intranet-invoices.lt_Generate_Invoice_from]"
}
if {$cost_type_id == [im_cost_type_po]} {
    set target_cost_type_id [im_cost_type_bill]
    set generation_blurb "[lang::message::lookup $locale intranet-invoices.lt_Generate_Provider_Bil]"
}
 
if {$invoice_or_quote_p} {
    # A Customer document
    set customer_or_provider_join "and ci.customer_id = c.company_id"
    set provider_company "Customer"
} else {
    # A provider document
    set customer_or_provider_join "and ci.provider_id = c.company_id"
    set provider_company "Provider"
}
 
if {!$invoice_or_quote_p} { set company_project_nr_exists 0}
 
 
# Check if this is a timesheet invoice and enable the timesheet report link.
# This links allows the user to extract a detailed list of included hours.
set cost_object_type [db_string cost_object_type "select object_type from acs_objects where object_id = :invoice_id" -default ""]
set timesheet_report_enabled_p 0
if {"im_timesheet_invoice" == $cost_object_type} {
    if {$cost_type_id == [im_cost_type_invoice]} {
	set timesheet_report_enabled_p 1
    }
}
 
 
# ---------------------------------------------------------------
# Find out if the invoice is associated with a _single_ project
# or with more then one project. Only in the case of exactly one
# project we can access the "customer_project_nr" for the invoice.
# ---------------------------------------------------------------
 
set related_projects_sql "
        select distinct
	   	r.object_id_one as project_id,
		p.project_name,
		p.project_nr,
		p.parent_id,
		trim(both p.company_project_nr) as customer_project_nr
	from
	        acs_rels r,
		im_projects p
	where
		r.object_id_one = p.project_id
	        and r.object_id_two = :invoice_id
"
 
set related_projects {}
set related_project_nrs {}
set related_project_names {}
set related_customer_project_nrs {}
 
set num_related_projects 0
db_foreach related_projects $related_projects_sql {
    lappend related_projects $project_id
    if {"" != $project_nr} { 
	lappend related_project_nrs $project_nr 
    }
    if {"" != $project_name} { 
	lappend related_project_names $project_name 
    }
 
    # Check of the "customer project nr" of the superproject, as the PMs
    # are probably too lazy to maintain it in the subprojects...
    set cnt 0
    while {[string equal "" $customer_project_nr] && ![string equal "" $parent_id] && $cnt < 10} {
	set customer_project_nr [db_string custpn "select company_project_nr from im_projects where project_id = :parent_id" -default ""]
	set parent_id [db_string parentid "select parent_id from im_projects where project_id = :parent_id" -default ""]
	incr cnt
    }
    if {"" != $customer_project_nr} { 
	lappend related_customer_project_nrs $customer_project_nr 
    }
    incr num_related_projects
}
 
set rel_project_id 0
if {1 == [llength $related_projects]} {
    set rel_project_id [lindex $related_projects 0]
}
 
 
# ---------------------------------------------------------------
# Get everything about the "internal" company
# ---------------------------------------------------------------
 
db_1row internal_company_info "
	select
		c.company_name as internal_name,
		c.company_path as internal_path,
		c.vat_number as internal_vat_number,
		c.site_concept as internal_web_site,
		im_name_from_user_id(c.manager_id) as internal_manager_name,
		im_email_from_user_id(c.manager_id) as internal_manager_email,
		c.primary_contact_id as internal_primary_contact_id,
		im_name_from_user_id(c.primary_contact_id) as internal_primary_contact_name,
		im_email_from_user_id(c.primary_contact_id) as internal_primary_contact_email,
		c.accounting_contact_id as internal_accounting_contact_id,
		im_name_from_user_id(c.accounting_contact_id) as internal_accounting_contact_name,
		im_email_from_user_id(c.accounting_contact_id) as internal_accounting_contact_email,
		o.office_name as internal_office_name,
		o.fax as internal_fax,
		o.phone as internal_phone,
		o.address_line1 as internal_address_line1,
		o.address_line2 as internal_address_line2,
		o.address_city as internal_city,
		o.address_state as internal_state,
		o.address_postal_code as internal_postal_code,
		o.address_country_code as internal_country_code,
		cou.country_name as internal_country_name,
		paymeth.category_description as internal_payment_method_desc
	from
		im_companies c
		LEFT OUTER JOIN im_offices o ON (c.main_office_id = o.office_id)
		LEFT OUTER JOIN country_codes cou ON (o.address_country_code = iso)
		LEFT OUTER JOIN im_categories paymeth ON (c.default_payment_method_id = paymeth.category_id)
	where
		c.company_path = 'internal'
"
 
 
# ---------------------------------------------------------------
# Get everything about the invoice
# ---------------------------------------------------------------
 
set query "
	select
		c.*,
		i.*,
		ci.effective_date::date + ci.payment_days AS due_date,
		ci.effective_date AS invoice_date,
		ci.cost_status_id AS invoice_status_id,
		ci.cost_type_id AS invoice_type_id,
		ci.template_id AS invoice_template_id,
		ci.*,
		ci.note as cost_note,
		ci.project_id as cost_project_id,
		to_date(to_char(ci.effective_date, 'YYYY-MM-DD'), 'YYYY-MM-DD') + ci.payment_days as calculated_due_date,
		im_cost_center_name_from_id(ci.cost_center_id) as cost_center_name,
		im_category_from_id(ci.cost_status_id) as cost_status,
		im_category_from_id(ci.cost_type_id) as cost_type, 
		im_category_from_id(ci.template_id) as template
	from
		im_invoices i,
		im_costs ci,
	        im_companies c
	where 
		i.invoice_id=:invoice_id
		and ci.cost_id = i.invoice_id
		$customer_or_provider_join
"
 
if { ![db_0or1row invoice_info_query $query] } {
 
    # Check if there is a cost item with this ID and forward
    set cost_exists_p [db_string cost_ex "select count(*) from im_costs where cost_id = :invoice_id"]
    if {$cost_exists_p} { 
	ad_returnredirect [export_vars -base "/intranet-cost/costs/new" {{form_mode display} {cost_id $invoice_id}}] 
    } else {
	ad_return_complaint 1 "[lang::message::lookup $locale intranet-invoices.lt_Cant_find_the_documen]"
    }
    return
}
 
# ---------------------------------------------------------------
# Get information about start- and end time of invoicing period
# ---------------------------------------------------------------
 
set invoice_period_start ""
set invoice_period_end ""
set timesheet_invoice_p 0
 
set query "
	select	ti.*,
		1 as timesheet_invoice_p
	from	im_timesheet_invoices ti
	where 	ti.invoice_id = :invoice_id
"
catch { db_1row timesheet_invoice_info_query $query } err_msg
 
 
# ---------------------------------------------------------------
# Get everything about our "internal" Office -
# identified as the "main_office_id" of the Internal company.
# ---------------------------------------------------------------
 
# ToDo: Isn't this included in the Internal company query above?
 
set cost_type_mapped [string map {" " "_"} $cost_type]
set cost_type_l10n [lang::message::lookup $locale intranet-invoices.$cost_type_mapped $cost_type]
 
# Fallback for empty office_id: Main Office
if {"" == $invoice_office_id} {
    set invoice_office_id $main_office_id
}
 
db_1row office_info_query "
	select *
	from im_offices
	where office_id = :invoice_office_id
"
 
 
# ---------------------------------------------------------------
# Get everything about the contact person.
# ---------------------------------------------------------------
 
# Use the "company_contact_id" of the invoices as the main contact.
# Fallback to the accounting_contact_id and primary_contact_id
# if not present.
if {"" == $company_contact_id} { 
    set company_contact_id $accounting_contact_id
}
if {"" == $company_contact_id} { 
    set company_contact_id $primary_contact_id 
}
set org_company_contact_id $company_contact_id
 
set company_contact_name ""
set company_contact_email ""
set company_contact_first_names ""
set company_contact_last_name ""
 
db_0or1row accounting_contact_info "
	select
		im_name_from_user_id(person_id) as company_contact_name,
		im_email_from_user_id(person_id) as company_contact_email,
		first_names as company_contact_first_names,
		last_name as company_contact_last_name
	from	persons
	where	person_id = :company_contact_id
"
 
# Fields normally available from intranet-contacts.
# Set these fields if contacts is not installed:
if {![info exists salutation]} { set salutation "" }
if {![info exists user_position]} { set user_position "" }
 
# Get contact person's contact information
set contact_person_work_phone ""
set contact_person_work_fax ""
set contact_person_email ""
db_0or1row contact_info "
	select
		work_phone as contact_person_work_phone,
		fax as contact_person_work_fax,
		im_email_from_user_id(user_id) as contact_person_email
	from
		users_contact
	where
		user_id = :company_contact_id
"
 
# Set the email and name of the current user as internal contact
db_1row accounting_contact_info "
    select
	im_name_from_user_id(:user_id) as internal_contact_name,
	im_email_from_user_id(:user_id) as internal_contact_email,
	uc.work_phone as internal_contact_work_phone,
	uc.home_phone as internal_contact_home_phone,
	uc.cell_phone as internal_contact_cell_phone,
	uc.fax as internal_contact_fax,
	uc.wa_line1 as internal_contact_wa_line1,
	uc.wa_line2 as internal_contact_wa_line2,
	uc.wa_city as internal_contact_wa_city,
	uc.wa_state as internal_contact_wa_state,
	uc.wa_postal_code as internal_contact_wa_postal_code,
	uc.wa_country_code as internal_contact_wa_country_code
    from
	users u
	LEFT OUTER JOIN users_contact uc ON (u.user_id = uc.user_id)
    where
	u.user_id = :user_id
"
 
 
# ---------------------------------------------------------------
# Determine the language of the template from the template name
# ---------------------------------------------------------------
 
set template_type ""
if {0 != $render_template_id} {
 
    # Maintain compatibility with old convention "invoice-english.adp"
    # ToDo: Remove this compatibility with V4.0
    if {[regexp {english} $template]} { set locale en }
    if {[regexp {spanish} $template]} { set locale es }
    if {[regexp {german} $template]} { set locale de }
    if {[regexp {french} $template]} { set locale fr }
 
    # New convention, "invoice.en_US.adp"
    if {[regexp {(.*)\.([_a-zA-Z]*)\.([a-zA-Z][a-zA-Z][a-zA-Z])} $template match body loc template_type]} {
	set locale $loc
    }
}
 
# Check if the given locale throws an error
# Reset the locale to the default locale then
if {[catch {
    lang::message::lookup $locale "dummy_text"
} errmsg]} {
    set locale $user_locale
}
 
ns_log Notice "view.tcl: locale=$locale"
ns_log Notice "view.tcl: template_type=$template_type"
 
# ----------------------------------------------------------------------------------------
# Check if there are Dynamic Fields of type date and localize them 
# ----------------------------------------------------------------------------------------
 
set date_fields [list]
set column_sql "
        select  w.widget_name,
                aa.attribute_name
        from    im_dynfield_widgets w,
                im_dynfield_attributes a,
                acs_attributes aa
        where   a.widget_name = w.widget_name and
                a.acs_attribute_id = aa.attribute_id and
                aa.object_type = 'im_invoice' and
                w.widget_name = 'date'
"
db_foreach column_list_sql $column_sql {
    set y ${attribute_name}
    set z [lc_time_fmt [subst $${y}] "%x" $locale]
    set ${attribute_name} $z
}
 
# ---------------------------------------------------------------
# OOoo ODT Function
# Split the template into the outer template and the one for
# formatting the invoice lines.
# ---------------------------------------------------------------
 
 
if {"odt" == $template_type} {
 
 
#    ad_return_complaint 1 $date_qoute
 
    # Special ODT functionality: We need to parse the ODT template
    # in order to extract the table row that needs to be formatted
    # by the loop below.
 
    # ------------------------------------------------
    # Create a temporary directory for our contents
    set odt_tmp_path [ns_tmpnam]
    ns_log Notice "view.tcl: odt_tmp_path=$odt_tmp_path"
    ns_mkdir $odt_tmp_path
 
    # The document 
    set odt_zip "${odt_tmp_path}.odt"
    set odt_content "${odt_tmp_path}/content.xml"
    set odt_styles "${odt_tmp_path}/styles.xml"
 
    # ------------------------------------------------
    # Create a copy of the ODT
 
    # Determine the location of the template
    set invoice_template_path "$invoice_template_base_path/$template"
    ns_log Notice "view.tcl: invoice_template_path='$invoice_template_path'"
 
    # Create a copy of the template into the temporary dir
    ns_cp $invoice_template_path $odt_zip
 
    # Unzip the odt into the temorary directory
    exec unzip -d $odt_tmp_path $odt_zip 
 
    # ------------------------------------------------
    # Read the content.xml file
    set file [open $odt_content]
    fconfigure $file -encoding "utf-8"
    set odt_template_content [read $file]
    close $file
 
    # ------------------------------------------------
    # Search the <row> ...<cell>..</cell>.. </row> line
    # representing the part of the template that needs to
    # be repeated for every template.
 
    # Get the list of all "tables" in the document
    set odt_doc [dom parse $odt_template_content]
    set root [$odt_doc documentElement]
    set odt_table_nodes [$root selectNodes "//table:table"]
 
    # Search for the table that contains "@item_name_pretty"
    set odt_template_table_node ""
    foreach table_node $odt_table_nodes {
	set table_as_list [$table_node asList]
	if {[regexp {item_units_pretty} $table_as_list match]} { set odt_template_table_node $table_node }
    }
 
    # Deal with the the situation that we didn't find the line
    if {"" == $odt_template_table_node} {
	ad_return_complaint 1 "
		<b>Didn't find table including '@item_units_pretty'</b>:<br>
		We have found a valid OOoo template at '$invoice_template_path'.
		However, this template does not include a table with the value
		above.
	"
	ad_script_abort
    }
 
    # Seach for the 2nd table:table-row tag
    set odt_table_rows_nodes [$odt_template_table_node selectNodes "//table:table-row"]
    set odt_template_row_node ""
    set odt_template_row_count 0
    foreach row_node $odt_table_rows_nodes {
	set row_as_list [$row_node asList]
	if {[regexp {item_units_pretty} $row_as_list match]} { set odt_template_row_node $row_node }
	incr odt_template_row_count
    }
 
    if {"" == $odt_template_row_node} {
	ad_return_complaint 1 "
		<b>Didn't find row including '@item_units_pretty'</b>:<br>
		We have found a valid OOoo template at '$invoice_template_path'.
		However, this template does not include a row with the value
		above.
	"
	ad_script_abort
    }
 
    # Convert the tDom tree into XML for rendering
    set odt_row_template_xml [$odt_template_row_node asXML]
 
}
 
 
 
# ---------------------------------------------------------------
# Format Invoice date information according to locale
# ---------------------------------------------------------------
 
set invoice_date_pretty [lc_time_fmt $invoice_date "%x" $locale]
set calculated_due_date_pretty [lc_time_fmt $calculated_due_date "%x" $locale]
 
set invoice_period_start_pretty [lc_time_fmt $invoice_period_start "%x" $locale]
set invoice_period_end_pretty [lc_time_fmt $invoice_period_end "%x" $locale]
 
 
# ---------------------------------------------------------------
# Get more about the invoice's project
# ---------------------------------------------------------------
 
# We give priority to the project specified in the cost item,
# instead of associated projects.
if {"" != $cost_project_id && 0 != $cost_project_id} {
    set rel_project_id $cost_project_id
}
 
set project_short_name_default [db_string short_name_default "select project_nr from im_projects where project_id=:rel_project_id" -default ""]
set customer_project_nr_default ""
 
if {$company_project_nr_exists && $rel_project_id} {
 
    db_0or1row project_info_query "
    	select
    		p.company_project_nr as customer_project_nr_default
    	from
    		im_projects p
    	where
    		p.project_id = :rel_project_id
    "
}
 
# ---------------------------------------------------------------
# Check permissions
# ---------------------------------------------------------------
 
im_cost_permissions $user_id $invoice_id view read write admin
if {!$read} {
    ad_return_complaint "[lang::message::lookup $locale intranet-invoices.lt_Insufficient_Privileg]" "
    <li>[lang::message::lookup $locale intranet-invoices.lt_You_have_insufficient_1]<BR>
    [lang::message::lookup $locale intranet-invoices.lt_Please_contact_your_s]"
    ad_script_abort
}
 
# ---------------------------------------------------------------
# Page Title and Context Bar
# ---------------------------------------------------------------
 
set page_title [lang::message::lookup $locale intranet-invoices.One_cost_type]
set context_bar [im_context_bar [list /intranet-invoices/ "[lang::message::lookup $locale intranet-invoices.Finance]"] $page_title]
 
 
# ---------------------------------------------------------------
#
# ---------------------------------------------------------------
 
set comp_id $company_id
set query "
select
        pm_cat.category as invoice_payment_method,
	pm_cat.category_description as invoice_payment_method_desc
from 
        im_categories pm_cat
where
        pm_cat.category_id = :payment_method_id
"
if { ![db_0or1row category_info_query $query] } {
    set invoice_payment_method ""
    set invoice_payment_method_desc ""
}
 
set invoice_payment_method_key [lang::util::suggest_key $invoice_payment_method]
set invoice_payment_method_l10n [lang::message::lookup $locale intranet-core.$invoice_payment_method_key $invoice_payment_method]
 
 
# ---------------------------------------------------------------
# Determine the country name and localize
# ---------------------------------------------------------------
 
set country_name ""
if {"" != $address_country_code} {
    set query "
	select	cc.country_name
	from	country_codes cc
	where	cc.iso = :address_country_code"
    if { ![db_0or1row country_info_query $query] } {
	    set country_name $address_country_code
    }
    set country_name [lang::message::lookup $locale intranet-core.$country_name $country_name]
}
 
# ---------------------------------------------------------------
# Update the amount paid for this cost_item
# ---------------------------------------------------------------
 
# This is redundant now - The same calculation is done
# when adding/removing costs. However, there may be cases
# with manually added costs. ToDo: Not very, very clean
# solution.
im_cost_update_payments $invoice_id
 
 
# ---------------------------------------------------------------
# Payments list
# ---------------------------------------------------------------
 
set payment_list_html ""
if {[im_table_exists im_payments]} {
 
    set cost_id $invoice_id
    set payment_list_html "
	<form action=payment-action method=post>
	[export_form_vars cost_id return_url]
	<table border=0 cellPadding=1 cellspacing=1>
        <tr>
          <td align=middle class=rowtitle colspan=3>
	    [lang::message::lookup $locale intranet-invoices.Related_Payments]
	  </td>
        </tr>"
 
    set payment_list_sql "
select
	p.*,
        to_char(p.received_date,'YYYY-MM-DD') as received_date_pretty,
	im_category_from_id(p.payment_type_id) as payment_type
from
	im_payments p
where
	p.cost_id = :invoice_id
"
 
    set payment_ctr 0
    db_foreach payment_list $payment_list_sql {
	append payment_list_html "
        <tr $bgcolor([expr $payment_ctr % 2])>
          <td>
	    <A href=/intranet-payments/view?payment_id=$payment_id>
	      $received_date_pretty
 	    </A>
	  </td>
          <td>
	      $amount $currency
          </td>\n"
	if {$write} {
	    append payment_list_html "
            <td>
	      <input type=checkbox name=payment_id value=$payment_id>
            </td>\n"
	}
	append payment_list_html "
        </tr>\n"
	incr payment_ctr
    }
 
    if {!$payment_ctr} {
	append payment_list_html "<tr class=roweven><td colspan=2 align=center><i>[lang::message::lookup $locale intranet-invoices.No_payments_found]</i></td></tr>\n"
    }
 
 
    if {$write} {
	append payment_list_html "
        <tr $bgcolor([expr $payment_ctr % 2])>
          <td align=right colspan=3>
	    <input type=submit name=add value=\"[lang::message::lookup $locale intranet-invoices.Add_a_Payment]\">
	    <input type=submit name=del value=\"[lang::message::lookup $locale intranet-invoices.Del]\">
          </td>
        </tr>\n"
    }
    append payment_list_html "
	</table>
        </form>\n"
}
 
# ---------------------------------------------------------------
# 3. Select and format Invoice Items
# ---------------------------------------------------------------
 
set decoration_item_nr [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleItemNr" "" "align=center"]
set decoration_description [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleDescription" "" "align=left"]
set decoration_quantity [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleQuantity" "" "align=right"]
set decoration_unit [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleUnit" "" "align=left"]
set decoration_rate [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleRate" "" "align=right"]
set decoration_po_number [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitlePoNumber" "" "align=center"]
set decoration_our_ref [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleOurRef" "" "align=center"]
set decoration_amount [ad_parameter -package_id [im_package_invoices_id] "InvoiceDecorationTitleAmount" "" "align=right"]
 
 
# start formatting the list of sums with the header...
set invoice_item_html "<tr align=center>\n"
 
if {$show_our_project_nr && $show_leading_invoice_item_nr} {
    append invoice_item_html "
          <td class=rowtitle $decoration_item_nr>[lang::message::lookup $locale intranet-invoices.Line_no "#"]</td>
    "
}
 
append invoice_item_html "
          <td class=rowtitle $decoration_description>[lang::message::lookup $locale intranet-invoices.Description]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
"
 
if {$show_qty_rate_p} {
    append invoice_item_html "
          <td class=rowtitle $decoration_quantity>[lang::message::lookup $locale intranet-invoices.Qty]</td>
          <td class=rowtitle $decoration_unit>[lang::message::lookup $locale intranet-invoices.Unit]</td>
          <td class=rowtitle $decoration_rate>[lang::message::lookup $locale intranet-invoices.Rate]</td>
    "
}
 
if {$show_company_project_nr} {
    # Only if intranet-translation has added the field and param is set
    append invoice_item_html "
          <td class=rowtitle $decoration_po_number>[lang::message::lookup $locale intranet-invoices.Yr_Job__PO_No]</td>\n"
}
 
if {$show_our_project_nr} {
    # Only if intranet-translation has added the field and param is set
    append invoice_item_html "
          <td class=rowtitle $decoration_our_ref>[lang::message::lookup $locale intranet-invoices.Our_Ref]</td>
    "
}
 
append invoice_item_html "
          <td class=rowtitle $decoration_amount>[lang::message::lookup $locale intranet-invoices.Amount]</td>
        </tr>
"
 
set ctr 1
	set colspan [expr 2 + 3*$show_qty_rate_p + 1*$show_company_project_nr + $show_our_project_nr]
 
set oo_table_xml ""
 
 
if { 0 == $item_list_type } {
	db_foreach invoice_items {} {
	    # $company_project_nr is normally related to each invoice item,
	    # because invoice items can be created based on different projects.
	    # However, frequently we only have one project per invoice, so that
	    # we can use this project's company_project_nr as a default
	    if {$company_project_nr_exists && "" == $company_project_nr} { 
		set company_project_nr $customer_project_nr_default
	    }
	    if {"" == $project_short_name} { 
		set project_short_name $project_short_name_default
	    }
 
	    set amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $amount+0] $rounding_precision] "" $locale]
	    set item_units_pretty [lc_numeric [expr $item_units+0] "" $locale]
	    set price_per_unit_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $price_per_unit+0] $rounding_precision] "" $locale]
 
	    append invoice_item_html "
		<tr $bgcolor([expr $ctr % 2])>
	    "
 
	    if {$show_leading_invoice_item_nr} {
	        append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2]) align=right>$sort_order</td>\n"
	    }
 
	    append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2])>$item_name</td>
	    "
	    if {$show_qty_rate_p} {
	        append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2]) align=right>$item_units_pretty</td>
	          <td $bgcolor([expr $ctr % 2]) align=left>[lang::message::lookup $locale intranet-core.$item_uom $item_uom]</td>
	          <td $bgcolor([expr $ctr % 2]) align=right>$price_per_unit_pretty&nbsp;$currency</td>
	        "
	    }
 
	    if {$show_company_project_nr} {
		# Only if intranet-translation has added the field
		append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2]) align=left>$company_project_nr</td>\n"
	    }
 
	    if {$show_our_project_nr} {
		append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2]) align=left>$project_short_name</td>\n"
	    }
 
	    append invoice_item_html "
	          <td $bgcolor([expr $ctr % 2]) align=right>$amount_pretty&nbsp;$currency</td>
		</tr>"
 
	    # Insert a new XML table row into OpenOffice document
	    if {"odt" == $template_type} {
 
		# Replace placeholders in the OpenOffice template row with values
		eval [template::adp_compile -string $odt_row_template_xml]
		set odt_row_xml $__adp_output
 
		# Parse the new row and insert into OOoo document
		set row_doc [dom parse $odt_row_xml]
		set new_row [$row_doc documentElement]
		$odt_template_table_node insertBefore $new_row $odt_template_row_node
 
	    }
 
	    incr ctr
	}
} else {
 
	set indent_level [db_string get_view_id "
			select 
				tree_level(children.tree_sortkey) - tree_level(parent.tree_sortkey) as level
		 	from
				im_projects parent,
				im_projects children
			where
				children.tree_sortkey between parent.tree_sortkey and tree_right(parent.tree_sortkey) and
				children.project_id in (select task_id from im_invoice_items where invoice_id=$invoice_id)
			order by 
				level DESC
			limit 1
	" -default 0]
 
	set invoice_items_sql "
		select 
			all_items.*,
			im_category_from_id(item_type_id) as item_type,
			im_category_from_id(item_uom_id) as item_uom,
			round(price_per_unit * item_units * $rf) / $rf as amount,
			to_char(round(price_per_unit * item_units * $rf) / $rf, '$cur_format' ) as amount_formatted
		from 
			(
			select
				parent.project_id as parent_id,
				parent.project_nr as parent_nr,
				parent.project_name as parent_name,
				children.project_id,
				children.project_nr,
				children.project_name,
				tree_level(children.tree_sortkey) - tree_level(parent.tree_sortkey) as level,
				t.task_id,
				(select item_units from im_invoice_items i where (t.task_id = i.task_id and i.invoice_id=$invoice_id)) as item_units,
				(select item_type_id from im_invoice_items i where (t.task_id = i.task_id and i.invoice_id=$invoice_id)) as item_type_id,
				(select i.item_uom_id from im_invoice_items i where (t.task_id = i.task_id and i.invoice_id=$invoice_id)) as item_uom_id,
				(select i.price_per_unit from im_invoice_items i where (t.task_id = i.task_id and i.invoice_id=$invoice_id)) as price_per_unit,
				parent.tree_sortkey as parent_tree_sortkey,
				children.tree_sortkey as children_tree_sortkey
		 	from
				im_projects parent,
				im_projects children
				LEFT OUTER JOIN im_timesheet_tasks t ON (children.project_id = t.task_id)
			where
				children.tree_sortkey between parent.tree_sortkey and tree_right(parent.tree_sortkey) and
				children.project_id in (select task_id from im_invoice_items where invoice_id=$invoice_id)
			UNION 
			select 
				0 as parent_id,
				'' as parent_nr,
				item_name as parent_name,
				0 as project_id,
				'' as project_nr,
				item_name as project_name,
				0 as level,
				task_id,
				item_units,
				item_type_id,
				item_uom_id,
				price_per_unit,
				'1111111111111111111111111111111111111' as parent_tree_sortkey,
				'1111111111111111111111111111111111111' as children_tree_sortkey
			from 
				im_invoice_items
			where  
				invoice_id=30822 and 
				task_id=-1
			) all_items
 
		ORDER BY 
			parent_tree_sortkey,
			children_tree_sortkey,
			project_id;
	"
 
	set old_parent_id -1
	set amount_sub_total 0
	db_foreach related_projects $invoice_items_sql {
		# if {"" == $material_name} { set material_name $default_material_name }
   		if { ("0"!=$ctr && $old_parent_id!=$parent_id && "0"!=$level && 0!=$amount_sub_total) || "-1"==$task_id } {
			 if { "NULL"!=$task_id } {
	    			append invoice_item_html "
					<tr><td class='invoiceroweven' colspan ='100' align='right'>
					[lc_numeric [im_numeric_add_trailing_zeros [expr $amount_sub_total+0] $rounding_precision] "" $locale]&nbsp;$currency</td></tr>
				"
				set amount_sub_total 0    		
			} else {
                                append invoice_item_html "<tr><td>[lang::message::lookup $locale intranet-timesheet2-invoices.No_Information]</td></tr>"
			}
   		}
		set indent ""
		set indent_level_item [expr $indent_level - $level]  
		for {set i 0} {$i < $indent_level_item} {incr i} { 
		    	append indent "&nbsp;&nbsp;" 
		}
		# this items is not related to a task; it has been created as part of the financial document
		if { "-1" == $task_id } { 
			set indent ""
		}
		# insert headers for every project
		if { $old_parent_id != $parent_id } {
	    	if { 0 != $level } {     	
		    		append invoice_item_html "<tr><td class='invoiceroweven'>$indent$parent_name </td></tr>"
		    		set old_parent_id $parent_id
				} else {
				    set amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $amount+0] $rounding_precision] "" $locale]
			    	set item_units_pretty [lc_numeric [expr $item_units+0] "" $locale]
		    		set price_per_unit_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $price_per_unit+0] $rounding_precision] "" $locale]
				append invoice_item_html "<tr>" 
				append invoice_item_html "<td class='invoiceroweven'>$indent$parent_name</td>" 
				if {$show_qty_rate_p} {
					append invoice_item_html "
					<td $bgcolor([expr $ctr % 2]) align=right>$item_units_pretty</td>
					<td $bgcolor([expr $ctr % 2]) align=left>[lang::message::lookup $locale intranet-core.$item_uom $item_uom]</td>
					<td $bgcolor([expr $ctr % 2]) align=right>$price_per_unit_pretty&nbsp;$currency</td>
		       			"
				}
				if {$show_company_project_nr} {
					# Only if intranet-translation has added the field
					# append invoice_item_html "<td align=left>$company_project_nr</td>\n"
		    		}
				if {$show_our_project_nr} {
					append invoice_item_html "
					<td $bgcolor([expr $ctr % 2]) align=left>$project_short_name</td>\n"
		    		}
				append invoice_item_html "<td $bgcolor([expr $ctr % 2]) align=right>$amount_pretty&nbsp;$currency</td></tr>"
					set amount_sub_total [expr $amount_sub_total + $amount]				
				}
		}
	    incr ctr
	} if_no_rows {
		append invoice_item_html "<tr><td>[lang::message::lookup $locale intranet-timesheet2-invoices.No_Information]</td></tr>"
    	}
	append invoice_item_html "<tr><td class='invoiceroweven' colspan ='100' align='right'>[lc_numeric [im_numeric_add_trailing_zeros [expr $amount_sub_total+0] $rounding_precision] "" $locale]&nbsp;$currency</td></tr>"
}
 
 
# ---------------------------------------------------------------
# Add subtotal + VAT + TAX = Grand Total
# ---------------------------------------------------------------
 
 
# Set these values to 0 in order to allow to calculate the
# formatted grand total
if {"" == $vat} { set vat 0}
if {"" == $tax} { set tax 0}
 
# Calculate grand total based on the same inner SQL
db_1row calc_grand_total ""
 
set subtotal_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $subtotal+0] $rounding_precision] "" $locale]
set vat_amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $vat_amount+0] $rounding_precision] "" $locale]
set tax_amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $tax_amount+0] $rounding_precision] "" $locale]
 
set vat_perc_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $vat+0] $rounding_precision] "" $locale]
set tax_perc_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $tax+0] $rounding_precision] "" $locale]
 
 
set grand_total_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $grand_total+0] $rounding_precision] "" $locale]
set total_due_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $total_due+0] $rounding_precision] "" $locale]
set discount_perc_pretty $discount_perc
set surcharge_perc_pretty $surcharge_perc
 
set discount_amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $discount_amount+0] $rounding_precision] "" $locale]
set surcharge_amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $surcharge_amount+0] $rounding_precision] "" $locale]
 
set colspan_sub [expr $colspan - 1]
 
# Add a subtotal
set subtotal_item_html "
        <tr> 
          <td class=roweven colspan=$colspan_sub align=right><B>[lang::message::lookup $locale intranet-invoices.Subtotal]</B></td>
          <td class=roweven align=right><B><nobr>$subtotal_pretty $currency</nobr></B></td>
        </tr>
"
 
# Discount
if {$discount_enabled_p && 0 != $discount_perc} {
    append subtotal_item_html "
        <tr> 
<!--          <td class=roweven colspan=$colspan_sub align=right>[lang::message::lookup $locale intranet-invoices.Discount]</td> -->
          <td class=roweven colspan=$colspan_sub align=right>$discount_text $discount_perc_pretty %</td>
          <td class=roweven align=right><nobr>$discount_amount_pretty $currency</nobr></td>
        </tr>
    "
}
 
# Surcharge
if {$surcharge_enabled_p && 0 != $surcharge_perc} {
    append subtotal_item_html "
        <tr> 
<!--          <td class=roweven colspan=$colspan_sub align=right>[lang::message::lookup $locale intranet-invoices.Surcharge]</td> -->
          <td class=roweven colspan=$colspan_sub align=right>$surcharge_text $surcharge_perc_pretty %</td>
          <td class=roweven align=right><nobr>$surcharge_amount_pretty $currency</nobr></td>
        </tr>
    "
}
 
# New Subtotal line
if {$discount_enabled_p || $surcharge_enabled_p} {
    append subtotal_item_html "
        <tr> 
          <td class=roweven colspan=$colspan_sub align=right><B>[lang::message::lookup $locale intranet-invoices.GrandTotal "Grand Total"]</B></td>
          <td class=roweven align=right><B><nobr>$grand_total_pretty $currency</nobr></B></td>
        </tr>
    "
}
 
if {"" != $vat && 0 != $vat} {
    append subtotal_item_html "
        <tr>
          <td class=roweven colspan=$colspan_sub align=right>[lang::message::lookup $locale intranet-invoices.VAT]: $vat_perc_pretty %&nbsp;</td>
          <td class=roweven align=right>$vat_amount_pretty $currency</td>
        </tr>
"
} else {
    append subtotal_item_html "
        <tr>
          <td class=roweven colspan=$colspan_sub align=right>[lang::message::lookup $locale intranet-invoices.VAT]: 0 %&nbsp;</td>
          <td class=roweven align=right>0 $currency</td>
        </tr>
"
}
 
if {"" != $tax && 0 != $tax} {
    append subtotal_item_html "
        <tr> 
          <td class=roweven colspan=$colspan_sub align=right>[lang::message::lookup $locale intranet-invoices.TAX]: $tax_perc_pretty  %&nbsp;</td>
          <td class=roweven align=right>$tax_amount_pretty $currency</td>
        </tr>
    "
}
 
append subtotal_item_html "
        <tr> 
          <td class=roweven colspan=$colspan_sub align=right><b>[lang::message::lookup $locale intranet-invoices.Total_Due]</b></td>
          <td class=roweven align=right><b><nobr>$total_due_pretty $currency</nobr></b></td>
        </tr>
"
 
set payment_terms_html "
        <tr>
	  <td valign=top class=rowplain>[lang::message::lookup $locale intranet-invoices.Payment_Terms]</td>
          <td valign=top colspan=[expr $colspan-1] class=rowplain> 
            [lang::message::lookup $locale intranet-invoices.lt_This_invoice_is_past_]
          </td>
        </tr>
"
 
set payment_method_html "
        <tr>
	  <td valign=top class=rowplain>[lang::message::lookup $locale intranet-invoices.Payment_Method_1]</td>
          <td valign=top colspan=[expr $colspan-1] class=rowplain> $invoice_payment_method_desc</td>
        </tr>
"
 
set canned_note_html ""
if {$canned_note_enabled_p} {
 
    set canned_note_sql "
                select  c.aux_string1 as canned_note
                from    im_dynfield_attr_multi_value v,
			im_categories c
                where   object_id = :invoice_id
			and v.value::integer = c.category_id
    "
    set canned_notes ""
    db_foreach canned_notes $canned_note_sql {
	append canned_notes "$canned_note\n"
    }
 
    set canned_note_html "
        <tr>
	  <td valign=top class=rowplain>[lang::message::lookup $locale intranet-invoices.Canned_Note "Canned Note"]</td>
          <td valign=top colspan=[expr $colspan-1]>
	    &lt;pre><span style=\"font-family: verdana, arial, helvetica, sans-serif\">$canned_notes</font>&lt;/pre>
	  </td>
        </tr>
    "
}
 
 
set note_html "
        <tr>
	  <td valign=top class=rowplain>[lang::message::lookup $locale intranet-invoices.Note]</td>
          <td valign=top colspan=[expr $colspan-1]>
	    &lt;pre><span style=\"font-family: verdana, arial, helvetica, sans-serif\">$cost_note</font>&lt;/pre>
	  </td>
        </tr>
"
 
set terms_html ""
if {$cost_type_id == [im_cost_type_invoice] || $cost_type_id == [im_cost_type_bill]} {
    set terms_html [concat $payment_terms_html $payment_method_html]
}
append terms_html "$canned_note_html $note_html"
 
set item_list_html [concat $invoice_item_html $subtotal_item_html]
set item_html [concat $item_list_html $terms_html]
 
 
# ---------------------------------------------------------------
# Special Output: Format using a template and/or send out as PDF
# ---------------------------------------------------------------
 
# Use a specific template ("render_template_id") to render the "preview"
# of this invoice
if {0 != $render_template_id || "" != $send_to_user_as} {
 
    # New template type: OpenOffice document
    if {"odt" == $template_type} { set output_format "odt" }
 
    if {"" == $template} {
	ad_return_complaint "$cost_type Template not specified" "
	<li>You haven't specified a template for your $cost_type."
	return
    }
 
    set invoice_template_path "$invoice_template_base_path/$template"
    if {![file isfile $invoice_template_path] || ![file readable $invoice_template_path]} {
	ad_return_complaint "Unknown $cost_type Template" "
	<li>$cost_type template '$invoice_template_path' doesn't exist or is not readable
	for the web server. Please notify your system administrator."
	return
    }
 
    # Render the page using the template
    # Always, as HTML is the input for the PDF converter
    set invoices_as_html [ns_adp_parse -file $invoice_template_path]
 
    if {$output_format == "html" } {
 
	# HTML preview or email
	if {"" != $send_to_user_as} {
	    # Redirect to mail sending page:
	    # Add the rendered invoice to the form variables
	    ns_log Notice "view.tcl: html sending email"
	    rp_form_put invoice_html $invoices_as_html
	    rp_internal_redirect notify
	    ad_script_abort
 
	} else {
 
	    # Show invoice using template
	    ns_log Notice "view.tcl: html showing template"
	    db_release_unused_handles
	    ns_return 200 text/html $invoices_as_html
	    ad_script_abort
	}
 
    }
 
 
    # PDF output
    if {$output_format == "pdf" && $pdf_enabled_p} {
 
	ns_log Notice "view.tcl: pdf output format"
	set result [im_html2pdf $invoices_as_html]
	set tmp_pdf_file [lindex $result 0]
	set errlist [lindex $result 1]
 
	if {[llength $errlist] > 0} {
	    # Delete the temp file
	    im_html2pdf_read_file -delete_file_p 1 $tmp_pdf_file
 
	    # Return the error
	    ad_return_complaint 1 $errlist
	    ad_script_abort
	}
 
	# Write PDF out - either as preview or as email
	if {"" != $send_to_user_as} {
	    # Redirect to mail sending page:
	    # Add the rendered invoice to the form variables
	    ns_log Notice "view.tcl: pdf send out"
	    rp_form_put invoice_pdf $binary_content
	    rp_internal_redirect notify
	    ad_script_abort
 
	} else {
 
	    # PDF Preview
	    ns_log Notice "view.tcl: pdf preview"
	    db_release_unused_handles
	    ns_returnfile 200 application/pdf $tmp_pdf_file
	    catch { file delete $tmp_pdf_file } err
	    ad_script_abort
	}
    }
 
    # OpenOffice Output
    if {$output_format == "odt"} {
 
	ns_log Notice "view.tcl: odf formatting"
	# ------------------------------------------------
        # setup and constants
 
	if {$internal_path != "internal"} {
	    set internal_tax_id "208 171 00202"
	} else {
	    set internal_tax_id "208 120 20138"
	}
 
	# ------------------------------------------------
	# Delete the original template row, which is duplicate
	$odt_template_table_node removeChild $odt_template_row_node
 
	# ------------------------------------------------
        # Process the content.xml file
 
	set odt_template_content [$root asXML]
 
	# Perform replacements
        eval [template::adp_compile -string $odt_template_content]
        set content $__adp_output
 
	# set date_quote "ccc"
	# ad_return_complaint 1 $__adp_output
	# ad_return_complaint 1 $odt_template_content
 
	# Save the content to a file.
	set file [open $odt_content w]
	fconfigure $file -encoding "utf-8"
	puts $file $content
	flush $file
	close $file
 
 
	# ------------------------------------------------
        # Process the styles.xml file
 
        set file [open $odt_styles]
	fconfigure $file -encoding "utf-8"
        set style_content [read $file]
        close $file
 
        # Perform replacements
        eval [template::adp_compile -string $style_content]
        set style $__adp_output
 
	# Save the content to a file.
	set file [open $odt_styles w]
	fconfigure $file -encoding "utf-8"
	puts $file $style
	flush $file
	close $file
 
	# ------------------------------------------------
        # Replace the files inside the odt file by the processed files
 
	# The zip -j command replaces the specified file in the zipfile 
	# which happens to be the OpenOffice File. 
	ns_log Notice "view.tcl: before zipping"
	exec zip -j $odt_zip $odt_content
	exec zip -j $odt_zip $odt_styles
 
        db_release_unused_handles
 
	# ------------------------------------------------
        # Return the file
	ns_log Notice "view.tcl: before returning file"
        set outputheaders [ns_conn outputheaders]
        ns_set cput $outputheaders "Content-Disposition" "attachment; filename=${invoice_nr}.odt"
        ns_returnfile 200 application/odt $odt_zip
 
	# ------------------------------------------------
        # Delete the temporary files
 
	# delete other tmpfiles
	# ns_unlink "${dir}/$document_filename"
	# ns_unlink "${dir}/$content.xml"
	# ns_unlink "${dir}/$style.xml"
	# ns_unlink "${dir}/document.odf"
	# ns_rmdir $dir
	ad_script_abort
 
    }
 
    ad_return_complaint 1 "Internal Error - No output format specified"
 
} 
 
 
# ---------------------------------------------------------------------
# Sub-Navbar
# ---------------------------------------------------------------------
 
# Choose the right subnavigation bar
#
if {[llength $related_projects] != 1} {
    set sub_navbar [im_costs_navbar "none" "/intranet-invoices/index" "" "" [list] ""]
} else {
    set project_id [lindex $related_projects 0]
    set bind_vars [ns_set create]
    ns_set put $bind_vars project_id $project_id
    set parent_menu_id [db_string parent_menu "select menu_id from im_menus where label='project'" -default 0]
    set menu_label "project_finance"
    set sub_navbar [im_sub_navbar \
                        -components \
                        -base_url "/intranet/projects/view?project_id=$project_id" \
                        $parent_menu_id \
                        $bind_vars "" "pagedesriptionbar" $menu_label]
}
 
# ---------------------------------------------------------------------
# correct problem created by -r 1.33 view.adp 
# ---------------------------------------------------------------------
 
if {$cost_type_id == [im_cost_type_po]} {
   set customer_id $comp_id
}
 
# ---------------------------------------------------------------------
# ERR mess from intranet-trans-invoices
# ---------------------------------------------------------------------
 
if { "" != $err_mess } {
    set err_mess [lang::message::lookup "" $err_mess "Document Nr. not available anymore, please note and verify newly assigned number"]
}
 
 
 
 
# ---------------------------------------------------------------------
# Dynfields
# ---------------------------------------------------------------------
 
set extra_selects [list "0 as zero"]
set date_fields [list]
 
set column_sql "
        select  w.deref_plpgsql_function,
                aa.attribute_name,
		w.widget_name
        from    im_dynfield_widgets w,
                im_dynfield_attributes a,
                acs_attributes aa
        where   a.widget_name = w.widget_name and
                a.acs_attribute_id = aa.attribute_id and
                aa.object_type = 'im_invoice'
"
db_foreach column_list_sql $column_sql {
	if { "date" == $widget_name } {
	   lappend date_fields $attribute_name      
	}
	lappend extra_selects "${deref_plpgsql_function}($attribute_name) as ${attribute_name}_deref"
}
 
set extra_selects [join $extra_selects ",\n\t"]
 
set query "
select
        $extra_selects
from
        im_invoices p
where
        p.invoice_id=:invoice_id
 
"
 
if { ![db_0or1row invoice_info_query $query] } {
    # no dynfields - deactivate tab view 
}
 
set project_base_data_html "
                        <table border=0 cellpadding='10px' cellspacing='10px'>
                          <tr>
                            <!--<td>[_ intranet-core.Project_name]</td>-->
                            <td><b>Attribute</b></td>
                            <td><b>Value</b></td>
                          </tr>"
set column_sql "
        select
                aa.pretty_name,
                aa.attribute_name
        from
                im_dynfield_widgets w,
                acs_attributes aa,
                im_dynfield_attributes a
                LEFT OUTER JOIN (
                        select *
                        from im_dynfield_layout
                        where page_url = ''
                ) la ON (a.attribute_id = la.attribute_id)
        where
                a.widget_name = w.widget_name and
                a.acs_attribute_id = aa.attribute_id and
                aa.object_type = 'im_invoice'
        order by
                coalesce(la.pos_y,0), coalesce(la.pos_x,0)
"
 
 
db_foreach column_list_sql $column_sql {
    set var ${attribute_name}_deref
    set value [expr $$var]
    if {"" != [string trim $value]} {
                append project_base_data_html "
                  <tr>
                    <td>[lang::message::lookup "" intranet-core.$attribute_name $pretty_name]</td>
                    <td>$value</td>
                  </tr>
                "
    }
}
 
 
append project_base_data_html "</table>"

———————————————————————————-
c) Patch /web/projop/packages/intranet-invoices/www/view.adp
———————————————————————————-

Here’s the complete file /packages/intranet-invoices/www/view.adp
based on V3.5 – cvs branch: b3-5-0-patches:

<master src="../../intranet-core/www/master">
<property name="title">@page_title;noquote@</property>
<property name="main_navbar_label">finance</property>
<property name="sub_navbar">@sub_navbar;noquote@</property>
 
<if @show_dynfield_tab_p@ eq 1>
 
	<script src="http://yui.yahooapis.com/2.8.2r1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
	<script src="http://yui.yahooapis.com/2.8.2r1/build/element/element-min.js"></script>
	<script src="http://yui.yahooapis.com/2.8.2r1/build/tabview/tabview-min.js"></script>
	<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.8.2r1/build/tabview/assets/skins/sam/tabview.css">
 
	<script type="text/javascript">
		var myTabs = new YAHOO.widget.TabView("demo");
	</script>
 
	<div class="yui-skin-sam">
 
	<div id="demo" class="yui-navset">
	    <ul class="yui-nav">
        	<li class="selected"><a href="#tab1"><em>Invoice</em></a></li>
	        <li><a href="#tab2"><em>Dynamic Invoice Elements</em></a></li>
 	   </ul>
 
	<div class="yui-content">
	<div>
</if>
 
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span style="color:red;font-weight:bold;">@err_mess@</span>
 
<table cellpadding=1 cellspacing=1 border=0>
<tr valign=top>
  <td>
 
	  <%= [im_invoices_object_list_component $user_id $invoice_id $read $write $return_url] %>
  </td>
  <td>
    @payment_list_html;noquote@
  </td>
  <td>
	<table border=0 cellPadding=1 cellspacing=1>
	  <tr class=rowtitle>
	    <td colspan=2 class=rowtitle>#intranet-invoices.Admin_Links#</td>
	  </tr>
	  <tr>
	    <td>
 
	<ul>
	<li>
	  <% set render_template_id $template_id %>
	  <% set preview_vars [export_url_vars invoice_id render_template_id return_url] %>
	  <A HREF="/intranet-invoices/view?@preview_vars@">
		<%= [lang::message::lookup "" intranet-invoices.Preview_using_template "Preview using template"] %>
	  </A>
 
	<li>
	  <% set render_template_id $template_id %>
	  <% set preview_vars [export_url_vars invoice_id render_template_id return_url] %>
	  <A HREF="/intranet-invoices/view?@preview_vars@&item_list_type=1">
		<%= [lang::message::lookup "" intranet-invoices.Preview_using_template "Preview using template with task information"] %>
	  </A>
 
<if @pdf_enabled_p@>
	<li>
	  <% set render_template_id $template_id %>
	  <% set preview_vars [export_url_vars invoice_id render_template_id return_url] %>
	  <A HREF="/intranet-invoices/view?@preview_vars@&output_format=pdf">
		<%= [lang::message::lookup "" intranet-invoices.Preview_as_PDF "Preview as PDF"] %>
	  </A>
</if>
 
<if @timesheet_report_enabled_p@>
 
	<li>
	  <% 
		set level_of_details [parameter::get -package_id [apm_package_id_from_key intranet-invoices] -parameter LevelOfDetailsTimesheetHoursReport -default 4]
		set ts_url [export_vars -base $timesheet_report_url {{level_of_detail $level_of_details} {invoice_id $invoice_id}}] 	      %>
	  <A HREF="@ts_url;noquote@">
		<%= [lang::message::lookup "" intranet-invoices.Show_Included_Timesheet_Hours "Show Included Timesheet Hours"] %>
	  </A>
 
</if>
 
 
<if @admin@>
	<if @cost_type_id@ eq @quote_cost_type_id@>
	<li>
		<% set blurb [lang::message::lookup $locale intranet-invoices.Generate_Invoice_from_Quote "Generate Invoice from Quote"] %>
		<% set source_invoice_id $invoice_id %>
		<% set target_cost_type_id [im_cost_type_invoice] %>
		<% set gen_vars [export_url_vars source_invoice_id target_cost_type_id return_url] %>
		<A HREF="/intranet-invoices/new-copy?@gen_vars@">@blurb@</A>
 
	<li>
		<% set blurb [lang::message::lookup $locale intranet-invoices.Generate_Delivery_Note_from_Quote "Generate Delivery Note from Quote"] %>
		<% set source_invoice_id $invoice_id %>
		<% set target_cost_type_id [im_cost_type_delivery_note] %>
		<% set gen_vars [export_url_vars source_invoice_id target_cost_type_id return_url] %>
		<A HREF="/intranet-invoices/new-copy?@gen_vars@">@blurb@</A>
	</if>
 
	<if @cost_type_id@ eq @delnote_cost_type_id@>
	<li>
		<% set blurb [lang::message::lookup $locale intranet-invoices.Generate_Invoice_from_DelNote "Generate Invoice from Delivery Note"] %>
		<% set source_invoice_id $invoice_id %>
		<% set target_cost_type_id [im_cost_type_invoice] %>
		<% set gen_vars [export_url_vars source_invoice_id target_cost_type_id return_url] %>
		<A HREF="/intranet-invoices/new-copy?@gen_vars@">@blurb@</A>
	</if>
 
 
	<if @cost_type_id@ eq @po_cost_type_id@>
	<li>
		<% set blurb [lang::message::lookup $locale intranet-invoices.Generate_Provider_Bill_from_Purchase_Order "Generate Provider Bill from Purchase Order"] %>
		<% set source_invoice_id $invoice_id %>
		<% set target_cost_type_id [im_cost_type_bill] %>
		<% set gen_vars [export_url_vars source_invoice_id target_cost_type_id return_url] %>
		<A HREF="/intranet-invoices/new-copy?@gen_vars@">@blurb@</A>
	</if>
</if>
 
 
 
<if @write@>
<!--
	<li>
	  <% set notify_vars [export_url_vars invoice_id return_url] %>
	  <A HREF="/intranet-invoices/notify?@notify_vars@">
	  <%= [lang::message::lookup "" intranet-invoices.Send_document_as_HTML_link "Send this %cost_type% as HTML link"] %>
	  </A>
-->
 
	<li>
	  <% set url [export_vars -base "/intranet-invoices/view" {invoice_id {render_template_id $template_id} {send_to_user_as "html"} return_url}] %>
	  <A HREF="@url@">
	  <%= [lang::message::lookup "" intranet-invoices.Send_document_as_HTML_attachment "Send this %cost_type% as HTML attachment"] %>
	  </A>
 
<if @pdf_enabled_p@>
	<li>
	  <% set url [export_vars -base "/intranet-invoices/view" {invoice_id {render_template_id $template_id} {send_to_user_as "pdf"} return_url}] %>
	  <A HREF="@url@">
	  <%= [lang::message::lookup "" intranet-invoices.Send_document_as_PDF_attachment "Send this %cost_type% as PDF attachment"] %>
	  </A>
</if>
 
</if>
 
 
<if 0>
<if @ubl_enabled_p@>
        <li>
          <% set render_template_id $template_id %>
          <% set preview_vars [export_url_vars invoice_id return_url] %>
          <A HREF="/intranet-ubl/document.xml?@preview_vars@">
                <%= [lang::message::lookup "" intranet-invoices.Export_as_XML
		"Export as XML"] %>
          </A>
          (<%= [lang::message::lookup "" intranet-invoices.See_kolon "See:"]
	  %>
          <A HREF="/intranet-ubl/doc/"><%= [lang::message::lookup ""
	  intranet-invoices.UBL_XML_Documentation "UBL-XML\
 Documentation"] %></a>)
</if>
</if>
 
	</ul>
 
	    </td>
	  </tr>
	</table>
  </td>
</tr>
</table>
 
<!-- Invoice Data and Receipient Tables -->
<table cellpadding=0 cellspacing=0 bordercolor=#6699CC border=0 width="100%">
  <tr valign=top> 
    <td>
 
	<table border=0 cellPadding=0 cellspacing=2 width="100%">
        <tr>
	  <td align=middle class=rowtitle colspan=2>#intranet-invoices.cost_type_Data#
          </td>
	</tr>
        <tr>
          <td  class=rowodd>#intranet-invoices.cost_type_nr#.:</td>
          <td  class=rowodd>@invoice_nr@</td>
        </tr>
        <tr> 
          <td  class=roweven>#intranet-invoices.cost_type_date#:</td>
          <td  class=roweven>@invoice_date_pretty@</td>
        </tr>
<if [apm_package_installed_p "intranet-cost-center"] >
        <tr> 
          <td  class=roweven><%= [lang::message::lookup "" intranet-cost.Cost_Center "Cost Center"] %>:</td>
          <td  class=roweven>@cost_center_name@</td>
        </tr>
</if>
 
<if @invoice_or_bill_p@>
        <tr> 
          <td  class=rowodd>#intranet-invoices.cost_type_due_date#</td>
          <td  class=rowodd>@due_date@</td>
	</tr>
 
        <tr> 
          <td class=roweven>#intranet-invoices.Payment_terms#</td>
          <td class=roweven>#intranet-invoices.lt_payment_days_days_dat#</td>
	</tr>
 
	<tr>
          <td class=rowodd>#intranet-invoices.Payment_Method#</td>
          <td class=rowodd>@invoice_payment_method@</td>
	</tr>
 
</if>
 
 
	<tr>
          <td class=roweven>#intranet-invoices.cost_type_template#</td>
          <td class=roweven>@template@</td>
	</tr>
 
	<tr>
          <td class=roweven>#intranet-invoices.cost_type_type_1#</td>
          <td class=roweven>@cost_type@</td>
        </tr>
 
        <tr> 
          <td class=rowodd>#intranet-invoices.cost_type_status#:</td>
          <td class=rowodd>@cost_status@</td>
        </tr>
 
	<tr><td colspan=2 align=right>
<if @write@>
	  <form action=new method=POST>
	    <%= [export_form_vars return_url invoice_id cost_type_id] %>
	    <input type=submit name=edit_invoice value='#intranet-invoices.Edit#'>
	    <input type=submit name=del_invoice value='#intranet-core.Delete#'>
	  </form>
</if>
	</td></tr>
	</table>
 
    </td>
    <td></td>
    <td align=right>
      <table border=0 cellspacing=2 cellpadding=0 width="100%">
 
        <tr><td align=center valign=top class=rowtitle colspan=2> #intranet-invoices.Recipient#</td></tr>
 
<if @invoice_or_quote_p@>
        <tr> 
          <td  class=rowodd>#intranet-invoices.Company_name#</td>
          <td  class=rowodd>
            <A href="/intranet/companies/view?company_id=@customer_id@">@company_name@</A>
          </td>
        </tr>
</if>
<else>
        <tr> 
          <td  class=rowodd>#intranet-invoices.Provider#</td>
          <td  class=rowodd>
            <A href="/intranet/companies/view?company_id=@provider_id@">@company_name@</A>
          </td>
        </tr>
</else>
 
        <tr> 
          <td  class=roweven>#intranet-invoices.VAT#</td>
          <td  class=roweven>@vat_number@</td>
        </tr>
        <tr> 
          <td  class=rowodd> #intranet-invoices.Contact#</td>
          <td  class=rowodd>
            <A href=/intranet/users/view?user_id=@org_company_contact_id@>@company_contact_name@</A>
          </td>
        </tr>
        <tr> 
          <td  class=roweven>#intranet-invoices.Adress#</td>
          <td  class=roweven>@address_line1@ <br> @address_line2@</td>
        </tr>
        <tr> 
          <td  class=rowodd>#intranet-invoices.Zip#</td>
          <td  class=rowodd>@address_postal_code@</td>
        </tr>
        <tr> 
          <td  class=roweven>#intranet-invoices.Country#</td>
          <td  class=roweven>@country_name@</td>
        </tr>
        <tr> 
          <td  class=rowodd>#intranet-invoices.Phone#</td>
          <td  class=rowodd>@phone@</td>
        </tr>
        <tr> 
          <td  class=roweven>#intranet-invoices.Fax#</td>
          <td  class=roweven>@fax@</td>
        </tr>
        <tr> 
          <td  class=rowodd>#intranet-invoices.Email#</td>
          <td  class=rowodd>@company_contact_email@</td>
        </tr>
      </table>
  </tr>
</table>
 
<table cellpadding=0 cellspacing=2 border=0 width="100%">
<tr><td align=right>
  <table cellpadding=1 cellspacing=2 border=0>
    @item_list_html;noquote@
  </table>
 
  <table cellpadding=1 cellspacing=2 border=0>
    @terms_html;noquote@
  </table>
 
</td></tr>
</table>
 
</div>
 
<if @show_dynfield_tab_p@ eq 1>
<div>
    <%= [im_table_with_title "" $project_base_data_html] %>
</div>
 
</div>
 
</if>
 
</div>
</div>

———————————————————————————-
e) Patch /web/projop/packages/intranet-invoices/www/new.tcl
———————————————————————————-

# /packages/intranet-invoices/www/new.tcl
#
# Copyright (C) 2003 - 2009 ]project-open[
#
# All rights reserved. Please check
# http://www.project-open.com/license/ for details.
 
# ---------------------------------------------------------------
# 1. Page Contract
# ---------------------------------------------------------------
ad_page_contract { 
    Receives the list of tasks to invoice, creates a draft invoice
    (status: "In Process") and displays it.
    Provides a button to advance to new-2.tcl, which takes the final
    steps of invoice generation by setting the state of the invoice
    to "Created" and the state of the associates im_tasks to "Invoiced".
 
    @param create_invoice_from_template
           Indicates that "Create Invoice" button was
           used to start creating an invoice from a Quote or a
           Provider Bill from a Purchase Order
 
    @author frank.bergmann@project-open.com
} {
    { include_task:multiple "" }
    { invoice_id:integer 0}
    { cost_type_id:integer "" }
    { customer_id:integer 0}
    { provider_id:integer 0}
    { project_id:integer 0}
    { cost_center_id:integer 0}
    { invoice_currency ""}
    { create_invoice_from_template ""}
    { return_url "/intranet-invoices/list"}
    del_invoice:optional
}
 
# ---------------------------------------------------------------
# 2. Defaults & Security
# ---------------------------------------------------------------
 
set user_id [ad_maybe_redirect_for_registration]
set show_cost_center_p [ad_parameter -package_id [im_package_invoices_id] "ShowCostCenterP" "" 0]
set current_url [im_url_with_query]
 
# Check if we have to forward to "new-copy":
if {"" != $create_invoice_from_template} {
    ad_returnredirect [export_vars -base "new-copy" {invoice_id cost_type_id}]
    ad_script_abort
}
 
# Check if we need to delete the invoice.
# We get there because the "Delete" button in view.tcl can
# only send to one target, which is this file...
if {[info exists del_invoice]} {
    # Calculate the new return_url, because the invoice itself
    # will dissappear...
    set return_url "/intranet-invoices/list"
    set project_id [db_string pid "select project_id from im_costs where cost_id = :invoice_id" -default 0]
    if {"" != $project_id && 0 != $project_id} {
	set view_name "finance"
	set return_url [export_vars -base "/intranet/projects/view" {project_id view_name}] 
    }
    ad_returnredirect [export_vars -base delete {invoice_id return_url}]
}
 
 
 
 
# Do we need the cost_center_id for creating a new invoice?
# This is necessary if the invoice_nr depends on the cost_center_id (profit center).
set cost_center_required_p [parameter::get_from_package_key -package_key "intranet-invoices" -parameter "NewInvoiceRequiresCostCenterP" -default 0]
if {$cost_center_required_p && 0 == $invoice_id && ($cost_center_id == "" || $cost_center_id == 0)} {
    ad_returnredirect [export_vars -base "new-cost-center-select" {
	{pass_through_variables { cost_type_id customer_id provider_id include_task project_id invoice_currency create_invoice_from_template invoice_id select_project} }
	include_task
	invoice_id
	cost_type_id 
	customer_id 
	provider_id 
	select_project
	project_id 
	invoice_currency 
	cost_center_id
	create_invoice_from_template 
	{ return_url $current_url}
    }]
}
 
# Permissions
if {0 == $invoice_id} {
 
    if {"" == $cost_type_id} {
	ad_return_complaint 1 "<li>You need to specify a Cost Type"
	return
    }
 
    # CostCenter Permissions:
    # We are about to create a new invoice - Check specific creation perms
    set create_cost_types [im_cost_type_write_permissions $user_id]
    if {[lsearch -exact $create_cost_types $cost_type_id] == -1} {
	ad_return_complaint "Insufficient Privileges" "
        <li>You don't have sufficient privileges to create a 
            [db_string t "select im_category_from_id(:cost_type_id)"]."
	return
    }
 
} else {
 
    # CostCenter Permissions:
    # The invoice already exists - Check invoice permissions
    im_cost_permissions $user_id $invoice_id view read write admin
    if {!$write} {
	ad_return_complaint "Insufficient Privileges" "
        <li>You don't have sufficient privileges to see this page."    
    }
}
 
 
set return_url [im_url_with_query]
set todays_date [db_string get_today "select to_char(sysdate,'YYYY-MM-DD') from dual"]
set page_focus "im_header_form.keywords"
set view_name "invoice_tasks"
 
set bgcolor(0) " class=roweven"
set bgcolor(1) " class=rowodd"
set required_field "<font color=red size=+1><B>*</B></font>"
set cost_note ""
set canned_note ""
set canned_note_id ""
 
set tax_format "90.9"
set vat_format "90.9"
 
set discount_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceDiscountFieldP" "" 0]
set surcharge_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceSurchargeFieldP" "" 0]
 
# Canned Notes is a field with multiple messages per invoice
set canned_note_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceCannedNoteP" "" 0]
 
# Should we show the "Tax" field?
set tax_enabled_p [ad_parameter -package_id [im_package_invoices_id] "EnabledInvoiceTaxFieldP" "" 1]
 
# Should we show a "Material" field for invoice lines?
set material_enabled_p [ad_parameter -package_id [im_package_invoices_id] "ShowInvoiceItemMaterialFieldP" "" 0]
set project_type_enabled_p [ad_parameter -package_id [im_package_invoices_id] "ShowInvoiceItemProjectTypeFieldP" "" 1]
 
# Show dynfields?
set show_dynfield_tab_p [ad_parameter -package_id [im_package_invoices_id] "DynamicFieldSupport" "" "0"]
 
# Tricky case: Sombebody has called this page from a project
# So we need to find out the company of the project and create
# an invoice from scratch, invoicing all project elements.
if {0 != $project_id} {
    db_1row customer_info "
	select
		c.*
	from
		im_projects p,
		im_companies c
	where
		project_id = :project_id
		and p.company_id = c.company_id
    "
}
 
 
# ---------------------------------------------------------------
# Determine whether it's an Invoice or a Bill
# ---------------------------------------------------------------
 
# Invoices and Quotes have a "Company" fields.
set invoice_or_quote_p [im_cost_type_is_invoice_or_quote_p $cost_type_id]
 
# Invoices and Bills have a "Payment Terms" field.
set invoice_or_bill_p [im_cost_type_is_invoice_or_bill_p $cost_type_id]
 
if {$invoice_or_quote_p} {
    if { 0 == $customer_id } {
	set company_id [db_string cost_type "select customer_id from im_costs where cost_id = :invoice_id" -default ""]    
    } else {
	set company_id $customer_id
    }
    set ajax_company_widget "customer_id"
    set custprov "customer"
} else {
    set company_id $provider_id
    set ajax_company_widget "provider_id"
    set custprov "provider"
}
 
# ad_return_complaint 1 $company_id
 
 
# ---------------------------------------------------------------
# 3. Gather invoice data
#	a: if the invoice already exists
# ---------------------------------------------------------------
 
# Check if we are editing an already existing invoice
#
if {$invoice_id} {
    # We are editing an already existing invoice
 
    db_1row invoices_info_query ""
 
    # Canned Notes is a field with multiple messages per invoice
    if {$canned_note_enabled_p} {
	    set canned_note_id [db_list canned_notes "
		select	value
		from	im_dynfield_attr_multi_value
		where	object_id = :invoice_id
	    "]
    }
 
    set cost_type [im_category_from_id $cost_type_id]
    set invoice_mode "exists"
    set page_title "[_ intranet-invoices.Edit_cost_type]"
    set button_text $page_title
    set context_bar [im_context_bar [list /intranet/invoices/ "[_ intranet-invoices.Finance]"] $page_title]
 
    # Check if there is a single currency being used in the invoice
    # and get it.
    # This should always be the case, but doesn't need to...
    if {"" == $invoice_currency} {
	catch {
	    db_1row invoices_currency_query "
		select distinct
			currency as invoice_currency
		from	im_invoice_items i
		where	i.invoice_id=:invoice_id"
	} err_msg
    }
 
} else {
 
# ---------------------------------------------------------------
# Setup the fields for a new invoice
# ---------------------------------------------------------------
 
    # Build the list of selected tasks ready for invoices
    set invoice_mode "new"
    set in_clause_list [list]
    set cost_type [im_category_from_id $cost_type_id]
    set button_text "[_ intranet-invoices.New_cost_type]"
    set page_title "[_ intranet-invoices.New_cost_type]"
    set context_bar [im_context_bar [list /intranet/invoices/ "[_ intranet-invoices.Finance]"] $page_title]
 
    set invoice_id [im_new_object_id]
    set invoice_nr [im_next_invoice_nr -cost_type_id $cost_type_id -cost_center_id $cost_center_id]
    set cost_status_id [im_cost_status_created]
    set effective_date $todays_date
    set payment_days [ad_parameter -package_id [im_package_cost_id] "DefaultCompanyInvoicePaymentDays" "" 30] 
    set due_date [db_string get_due_date "select sysdate+:payment_days from dual"]
    set vat 0
    set tax 0
    set discount_text ""
    set discount_perc 0
    set surcharge_text ""
    set surcharge_perc 0
    set note ""
    set cost_note ""
    set canned_note ""
    set canned_note_id ""
    set payment_method_id ""
    set template_id ""
    set company_contact_id [im_invoices_default_company_contact $company_id $project_id]
    set read_only_p "f"
 
    # Default for cost-centers - take the user's
    # dept from HR.
    if {0 == $cost_center_id} {
	set cost_center_id [im_costs_default_cost_center_for_user $user_id]
    }
}
 
if {"" == $invoice_currency} {
    set invoice_currency [ad_parameter -package_id [im_package_cost_id] "DefaultCurrency" "" "EUR"]
}
 
if {"t" == $read_only_p} {
    ad_return_complaint 1 "
        <b>[lang::message::lookup "" intranet-cost.Invoice_Read_Only "Read Only"]</b>:
        [lang::message::lookup "" intranet-cost.Invoice_Read_Only_Message "
                <p>This financial document is read only.</p>
                <p>This situation may happen if the document has already been exported
                to an external system or in similar cases.</p>
    "]
    "
    ad_script_abort
}
 
 
# ---------------------------------------------------------------
# Get default values for VAT and invoice_id from company
# ---------------------------------------------------------------
 
if {[im_column_exists im_companies default_invoice_template_id]} {
    if {0 == $vat} {
	set vat [db_string default_vat "select default_vat from im_companies where company_id = :company_id" -default "0"]
    }
 
    if {"" == $template_id} {
	set template_id [db_string default_template "select default_invoice_template_id from im_companies where company_id = :company_id" -default ""]
    }
 
    if {"" == $payment_method_id} {
	set payment_method_id [db_string default_payment_method "select default_payment_method_id from im_companies where company_id = :company_id" -default ""]
    }
 
    set company_payment_days [db_string default_payment_days "select default_payment_days from im_companies where company_id = :company_id" -default ""]
    if {"" != $company_payment_days} {
	set payment_days $company_payment_days
    }
}
 
if {[im_column_exists im_companies default_tax]} {
    if {0 == $tax} {
        set tax [db_string default_tax "select default_tax from im_companies where company_id = :company_id" -default "0"]
    }
}
 
 
# Get a reasonable default value for the invoice_office_id,
# either from the invoice or then from the company_main_office.
 
set invoice_office_id [db_string invoice_office_info "select invoice_office_id from im_invoices where invoice_id = :invoice_id" -default ""]
if {"" == $invoice_office_id} {
    set invoice_office_id [db_string company_main_office_info "select main_office_id from im_companies where company_id = :company_id" -default ""]
}
 
# ---------------------------------------------------------------
# Calculate the selects for the ADP page
# ---------------------------------------------------------------
 
set payment_method_select [im_invoice_payment_method_select payment_method_id $payment_method_id]
set template_select [im_cost_template_select template_id $template_id]
set status_select [im_cost_status_select cost_status_id $cost_status_id]
 
set type_select [im_cost_type_select cost_type_id $cost_type_id 0 "financial_doc"]
if {"" != $cost_type_id} {
    set type_select "
	<input type=hidden name=cost_type_id value=$cost_type_id>
	$cost_type
    "
}
 
set customer_select [im_company_select -tag_attributes {onchange "ajaxFunction();" onkeyup "ajaxFunction();"} customer_id $customer_id "" "CustOrIntl"]
set provider_select [im_company_select -tag_attributes {onchange "ajaxFunction();" onkeyup "ajaxFunction();"} provider_id $provider_id "" "Provider"]
set contact_select [im_company_contact_select company_contact_id $company_contact_id $company_id]
 
set invoice_address_label [lang::message::lookup "" intranet-invoices.Invoice_Address "Address"]
set invoice_address_select [im_company_office_select invoice_office_id $invoice_office_id $company_id]
 
set cost_center_label [lang::message::lookup "" intranet-invoices.Cost_Center "Cost Center"]
 
if {$show_cost_center_p} {
    set cost_center_select [im_cost_center_select -include_empty 1 -department_only_p 0 cost_center_id $cost_center_id $cost_type_id]
} else {
    set cost_center_hidden "<input type=hidden name=cost_center_id value=$cost_center_id>"
}
 
# ---------------------------------------------------------------
# 7. Select and format the sum of the invoicable items
# for a new invoice
# ---------------------------------------------------------------
 
 
# start formatting the list of sums with the header...
set task_sum_html "
        <tr align=center> 
          <td class=rowtitle>[_ intranet-invoices.Line]</td>
          <td class=rowtitle>[_ intranet-invoices.Description]</td>
"
if {$material_enabled_p} {
    append task_sum_html "<td class=rowtitle>[lang::message::lookup "" intranet-invoices.Material "Material"]</td>"
}
if {$project_type_enabled_p} {
    append task_sum_html "<td class=rowtitle>[lang::message::lookup "" intranet-invoices.Type "Type"]</td>"
}
append task_sum_html "
          <td class=rowtitle>[_ intranet-invoices.Units]</td>
          <td class=rowtitle>[_ intranet-invoices.UOM]</td>
          <td class=rowtitle>[_ intranet-invoices.Rate]</td>
        </tr>
"
 
 
 
if {[string equal $invoice_mode "new"]} {
 
    # Start formatting the "reference price list" as well, even though it's going
    # to be shown at the very bottom of the page.
    #
    set price_colspan 11
    set ctr 1
    set old_project_id 0
    set colspan 6
    set vat_colspan 6
    set target_language_id ""
 
} else {
 
# ---------------------------------------------------------------
# 8. Get the old invoice items for an already existing invoice
# ---------------------------------------------------------------
 
    set ctr 1
    set old_project_id 0
    set colspan 6
    set vat_colspan 6
    set target_language_id ""
    db_foreach invoice_item "" {
 
	append task_sum_html "
	<tr $bgcolor([expr $ctr % 2])> 
          <td>
	    <input type=text name=item_sort_order.$ctr size=2 value='$sort_order'>
	  </td>
          <td>
	    <input type=text name=item_name.$ctr size=40 value='[ns_quotehtml $item_name]'>
	  </td>
	"
	append task_sum_html "<input type=hidden name=item_task_id.$ctr value='$task_id'>"
 
	if {$material_enabled_p} {
	    append task_sum_html "<td>[im_material_select -max_option_len 100 item_material_id.$ctr $item_material_id]</td>"
	} else {
	    append task_sum_html "<input type=hidden name=item_material_id.$ctr value='$item_material_id'>"
	}
 
	if {$project_type_enabled_p} {
	    append task_sum_html "<td>[im_category_select "Intranet Project Type" item_type_id.$ctr $item_type_id]</td>"
	} else {
	    append task_sum_html "<input type=hidden name=item_type_id.$ctr value='$item_type_id'>"
	}
 
	append task_sum_html "
          <td align=right>
	    <input type=text name=item_units.$ctr size=4 value='$item_units'>
	  </td>
          <td align=right>
            [im_category_select "Intranet UoM" item_uom_id.$ctr $item_uom_id]
	  </td>
          <td align=right>
	    <nobr><input type=text name=item_rate.$ctr size=7 value='$price_per_unit'>[im_currency_select item_currency.$ctr $currency]</nobr>
	  </td>
        </tr>
	<input type=hidden name=item_project_id.$ctr value='$project_id'>
	"
	incr ctr
    }
}
 
 
# ---------------------------------------------------------------
# Add some empty new lines for editing purposes
# ---------------------------------------------------------------
 
for {set i 0} {$i < 3} {incr i} {
 
    append task_sum_html "
	<tr $bgcolor([expr $ctr % 2])> 
          <td>
	    <input type=text name=item_sort_order.$ctr size=2 value='$ctr'>
	  </td>
          <td>
	    <input type=text name=item_name.$ctr size=40 value=''>
	  </td>
    "
    append task_sum_html "<input type=hidden name=item_task_id.$ctr value='-1'>"
 
    if {$material_enabled_p} {
	append task_sum_html "<td>[im_material_select -max_option_len 100 item_material_id.$ctr ""]</td>"
    } else {
	append task_sum_html "<input type=hidden name=item_material_id.$ctr value=''>"
    }
 
    if {$project_type_enabled_p} {
	append task_sum_html "<td>[im_category_select "Intranet Project Type" item_type_id.$ctr ""]</td>"
    } else {
	append task_sum_html "<input type=hidden name=item_type_id.$ctr value=''>"
    }
 
    append task_sum_html "
          <td align=right>
	    <input type=text name=item_units.$ctr size=4 value='0'>
	  </td>
          <td align=right>
            [im_category_select "Intranet UoM" item_uom_id.$ctr 320]
	  </td>
          <td align=right>
            <!-- rate and currency need to be together so that the line doesn't break -->
	    <nobr><input type=text name=item_rate.$ctr size=7 value='0'>[im_currency_select item_currency.$ctr $invoice_currency]</nobr>
	  </td>
        </tr>
	<input type=hidden name=item_project_id.$ctr value=''>
    "
 
    incr ctr
}
 
# ---------------------------------------------------------------
# Pass along the number of projects related to this document
# ---------------------------------------------------------------
 
# fraber 080515: @CTP: the project_id comes from im_invoice_ITEMS,
# and causes trouble (adding an additional project to the list of
# related projects). Did this code (own_project_related) make ever
# sense?
 
set own_project_related ""
if {0 != $project_id} { set own_project_related "UNION select :project_id as project_id" }
 
set related_project_sql "
	select	object_id_one as project_id
	from	acs_rels r
	where	r.object_id_two = :invoice_id
	$own_project_related
"
 
set select_project_html ""
db_foreach related_project $related_project_sql {
	append select_project_html "<input type=hidden name=select_project value=$project_id>\n"
}
 
set sub_navbar [im_costs_navbar "none" "/intranet/invoices/index" "" "" [list]] 
 
db_release_unused_handles
 
 
# ---------------------------------------------------------------
# Set Dynfields
# ---------------------------------------------------------------
 
 
set form_id "invoices_dynfield"
 
template::form::create $form_id -has_submit 1
template::form::section $form_id ""
 
set object_type "im_invoice"
set dynfield_project_type_id [im_opt_val project_type_id]
if {[info exists project_id]} {
    set existing_project_type_id [db_string ptype "select project_type_id from im_projects where project_id = :project_id" -default 0]
    if {0 != $existing_project_type_id && "" != $existing_project_type_id} {
        set dynfield_project_type_id $existing_project_type_id
    }
}
 
set dynfield_invoice_id 0
if {[info exists invoice_id]} { set dynfield_invoice_id $invoice_id }
 
set apd ""
 
set dynfield_project_type_id ""
 
set field_cnt [im_dynfield::append_attributes_to_form \
    -object_subtype_id $dynfield_project_type_id \
    -object_type $object_type \
    -form_id $form_id \
    -object_id $dynfield_invoice_id \
		   ]
if {![info exists ajax_post_data]} { 
    set ajax_post_data ""
}

———————————————————————————-
f) Patch /web/projop/packages/intranet-invoices/www/new.adp
———————————————————————————-

<master src="../../intranet-core/www/master">
<property name="title">@page_title;noquote@</property>
<property name="main_navbar_label">finance</property>
<property name="sub_navbar">@sub_navbar;noquote@</property>
 
<if @show_dynfield_tab_p@ eq 1>
<script src="http://yui.yahooapis.com/2.8.2r1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/element/element-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/tabview/tabview-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/yahoo/yahoo-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/event/event-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/connection/connection_core-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.2r1/build/button/button-min.js"></script>
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.8.2r1/build/tabview/assets/skins/sam/tabview.css">
</if>
 
<% 
    # Determine a security token to authenticate the AJAX function
    set auto_login [im_generate_auto_login -user_id [ad_get_user_id]] 
%>
 
<script type="text/javascript">
 
 
 
var global_eval = true;
 
function evalReturnValue(type, value, mandatory_p) {
        switch (type)
        {
        case "date":
                if ('--' == value && 'f' == mandatory_p) {
                        return "";
                } else  if (isValidDate(value,'yyyy/mm/dd')){
                        return value;
                } else {
                        alert('Please verify date');
                        global_eval = false;
                        return "";
                }
        case "text":
                if ('' == value && 't' == mandatory_p) {
                        alert('Please provide value - mandatory fields');
                        global_eval = false;
                        return "";
                } else {
                        return value;
                }
        default:
                return value;
        }
}
function isValidDate(date_string, format) {
    //http://lawrence.ecorp.net/inet/samples/regexp-validate.php
    var days = [0,31,28,31,30,31,30,31,31,30,31,30,31];
    var year, month, day, date_parts = null;
    var rtrn = false;
    var decisionTree = {
        'm/d/y':{
            're':/^(\d{1,2})[./-](\d{1,2})[./-](\d{2}|\d{4})$/,
            'month': 1,'day': 2, year: 3
        },
        'mm/dd/yy':{
            're':/^(\d{1,2})[./-](\d{1,2})[./-](\d{2})$/,
            'month': 1,'day': 2, year: 3
        },
        'mm/dd/yyyy':{
            're':/^(\d{1,2})[./-](\d{1,2})[./-](\d{4})$/,
            'month': 1,'day': 2, year: 3
        },
        'y/m/d':{
            're':/^(\d{2}|\d{4})[./-](\d{1,2})[./-](\d{1,2})$/,
            'month': 2,'day': 3, year: 1
        },
        'yy/mm/dd':{
            're':/^(\d{1,2})[./-](\d{1,2})[./-](\d{1,2})$/,
            'month': 2,'day': 3, year: 1
        },
        'yyyy/mm/dd':{
            're':/^(\d{4})[./-](\d{1,2})[./-](\d{1,2})$/,
            'month': 2,'day': 3, year: 1
        }
    };
    var test = decisionTree[format];
    if (test) {
        date_parts = date_string.match(test.re);
        if (date_parts) {
            year = date_parts[test.year];
            month = date_parts[test.month];
            day = date_parts[test.day];
 
            test = (month == 2 &&
                    isLeapYear() &&
                    29 ||
                    days[month] || 0);
 
            rtrn = 1 <= day && day <= test;
        }
    }
    function isLeapYear() {
        return (year % 4 != 0 ? false :
            ( year % 100 != 0? true:
            ( year % 1000 != 0? false : true)));
    }
    return rtrn;
}//eof isValidDate
 
 
// YUI Tabs 
var myTabs = new YAHOO.widget.TabView("demo");
 
// 
 
function ltrim(str, chars) {
	chars = chars || "\\s";
	return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}
 
function ajaxFunction() {
    var xmlHttp1;
    var xmlHttp2;
    try {
	// Firefox, Opera 8.0+, Safari
	xmlHttp1=new XMLHttpRequest();
	xmlHttp2=new XMLHttpRequest();
    }
    catch (e) {
	// Internet Explorer
	try {
	    xmlHttp1=new ActiveXObject("Msxml2.XMLHTTP");
	    xmlHttp2=new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch (e) {
	    try {
		xmlHttp1=new ActiveXObject("Microsoft.XMLHTTP");
		xmlHttp2=new ActiveXObject("Microsoft.XMLHTTP");
	    }
	    catch (e) {
		alert("Your browser does not support AJAX!");
		return false;
	    }
	}
    }
 
    xmlHttp1.onreadystatechange = function() {
	if(xmlHttp1.readyState==4) {
	    // empty options
	    for (i = document.invoice.invoice_office_id.options.length-1; i >= 0; i--) { 
		document.invoice.invoice_office_id.remove(i); 
	    }
 
	    // loop through the komma separated list
	    var res1 = xmlHttp1.responseText;
	    var opts1 = res1.split("|");
	    for (i=0; i < opts1.length; i = i+2) {
		var newOpt = new Option(opts1[i+1], opts1[i], false, true);
		document.invoice.invoice_office_id.options[document.invoice.invoice_office_id.options.length] = newOpt;
	    }
	}
    }
 
    xmlHttp2.onreadystatechange = function() {
	if(xmlHttp2.readyState==4) {
	    // empty options
	    for (i = document.invoice.company_contact_id.options.length-1; i >= 0; i--) { 
		document.invoice.company_contact_id.remove(i); 
	    }
	    // loop through the komma separated list
	    var res2 = xmlHttp2.responseText;
	    var opts2 = res2.split("|");
	    // alert(opts2);	    
	    for (i=0; i < opts2.length; i = i+2) {
		//alert (opts2[i]);
		var newOpt = new Option(opts2[i+1], ltrim(opts2[i]), false, true);
		document.invoice.company_contact_id.options[document.invoice.company_contact_id.options.length] = newOpt;
	    }
	}
    }
 
    // get the company_id from the customer's drop-down
    var company_id = document.invoice.@ajax_company_widget@.value;
    xmlHttp1.open("GET","/intranet/offices/ajax-offices?user_id=@user_id@&auto_login=@auto_login@&company_id="+company_id,true);
    xmlHttp1.send(null);
    xmlHttp2.open("GET","/intranet/users/ajax-company-contacts?user_id=@user_id@&auto_login=@auto_login@&company_id="+company_id,true);
    xmlHttp2.send(null);
}
</script>
 
<if @show_dynfield_tab_p@ eq 1>
<div class="yui-skin-sam">
 
<div id="demo" class="yui-navset">
    <ul class="yui-nav">
        <li class="selected"><a href="#tab1"><em>Invoice</em></a></li>
        <li><a href="#tab2"><em>Dynamic Invoice Elements</em></a></li>
    </ul>            
 
<div class="yui-content">
<div>
 
</if>
 
<form action=new-2 name=invoice method=POST>
<%= [export_form_vars invoice_id return_url] %>
 
<!-- Include a list of projects related to this document -->
@select_project_html;noquote@
 
<if @cost_center_hidden@ defined>
@cost_center_hidden;noquote@
</if>
 
<table border=0 width="100%">
<tr><td>
 
  <table cellpadding=0 cellspacing=0 bordercolor=#6699CC border=0 width="100%">
    <tr valign=top> 
      <td>
 
        <table border=0 cellPadding=0 cellspacing=2 width="100%">
 
 
	        <tr><td align=middle class=rowtitle colspan=2>#intranet-invoices.cost_type_Data#</td></tr>
	        <tr>
	          <td class=rowodd>#intranet-invoices.cost_type_nr#</td>
	          <td class=rowodd> 
	            <input type=text name=invoice_nr size=15 value='@invoice_nr@'>
	          </td>
	        </tr>
	        <tr> 
	          <td class=roweven>#intranet-invoices.cost_type_date#</td>
	          <td class=roweven> 
	            <input type=text name=invoice_date size=15 value='@effective_date@'>
	          </td>
	        </tr>
<if @cost_center_select@ defined>
	        <tr> 
	          <td class=roweven>@cost_center_label@</td>
	          <td class=roweven>
		  @cost_center_select;noquote@
	          </td>
	        </tr>
</if>
	        <tr> 
	          <td class=roweven>#intranet-invoices.Payment_terms#</td>
	          <td class=roweven> 
	            <input type=text name=payment_days size=5 value='@payment_days@'>
	            #intranet-invoices.days#</td>
	        </tr>
	        <tr> 
	          <td class=rowodd>#intranet-invoices.Payment_Method#</td>
	          <td class=rowodd>@payment_method_select;noquote@</td>
	        </tr>
 
	        <tr> 
	          <td class=roweven> #intranet-invoices.cost_type_template#</td>
	          <td class=roweven>@template_select;noquote@</td>
	        </tr>
	        <tr> 
	          <td class=rowodd>#intranet-invoices.cost_type_status#</td>
	          <td class=rowodd>@status_select;noquote@</td>
	        </tr>
	        <tr> 
	          <td class=roweven>#intranet-invoices.cost_type_type#</td>
	          <td class=roweven>@type_select;noquote@</td>
	        </tr>
 
        </table>
 
      </td>
      <td></td>
      <td>
        <table border=0 cellspacing=2 cellpadding=0 width="100%">
 
<if @invoice_or_quote_p@>
<!-- Let the user select the company. Provider=Internal -->
 
		<tr>
		  <td align=center valign=top class=rowtitle colspan=2>#intranet-invoices.Company#</td>
		</tr>
		<tr>
		  <td class=roweven>#intranet-core.Customer#</td>
		  <td class=roweven>@customer_select;noquote@</td>
		</tr>
		<input type=hidden name=provider_id value=@provider_id@>
</if>
<else>
 
		<tr>
		  <td align=center valign=top class=rowtitle colspan=2>#intranet-invoices.Provider#</td>
		</tr>
		<tr>
		  <td class=roweven>#intranet-invoices.Provider_1#</td>
		  <td class=roweven>@provider_select;noquote@</td>
		</tr>
		<input type=hidden name=customer_id value=@customer_id@>
</else>
 
 
		<tr>
		  <td class=rowodd>@invoice_address_label@</td>
		  <td class=rowodd>@invoice_address_select;noquote@</td>
		</tr>
 
		<tr>
		  <td class=rowodd>#intranet-core.Contact#</td>
		  <td class=rowodd>@contact_select;noquote@</td>
		</tr>
 
<if @canned_note_enabled_p@>
		<tr>
		  <td class=roweven><%= [lang::message::lookup "" intranet-invoices.Canned_Note "Canned Note"] %></td>
	          <td class=roweven>
<if 0>
		    <%= [im_category_select -translate_p 0 -include_empty_p 1 -include_empty_name "-- Please Select --" -plain_p 1 -cache_interval 0 "Intranet Invoice Canned Note" canned_note_id $canned_note_id] %>
 
</if>
<else>
		    <%= [im_category_select_multiple -translate_p 0 "Intranet Invoice Canned Note" canned_note_id $canned_note_id 3] %>
</else>
		  </td>
		</tr>
</if>
		<tr>
		  <td class=roweven>#intranet-invoices.Note#</td>
	          <td class=roweven>
		    <textarea name=note rows=6 cols=40 wrap="<%=[im_html_textarea_wrap]%>">@cost_note@</textarea>
		  </td>
		</tr>
 
 
        </table>
    </tr>
  </table>
 
</td></tr>
<tr><td>
 
  <table width="100%" align=right border=0>
    <tr>
      <td align=right>
 
 	<table border=0 cellspacing=2 cellpadding=1 width="100%">
	<!-- the list of task sums, distinguised by type and UOM -->
	@task_sum_html;noquote@
 
<if @discount_enabled_p@>
        <tr>
          <td> 
          </td>
          <td colspan=99 align=right> 
            <table border=0 cellspacing=1 cellpadding=0>
              <tr> 
                <td>#intranet-invoices.Discount# &nbsp;</td>
                <td><input type=text name=discount_text value="@discount_text@"> </td>
                <td><input type=text name=discount_perc value="@discount_perc@" size=4> % &nbsp;</td>
              </tr>
            </table>
          </td>
        </tr>
</if>
<if @surcharge_enabled_p@>
        <tr>
          <td> 
          </td>
          <td colspan=99 align=right> 
            <table border=0 cellspacing=1 cellpadding=0>
              <tr> 
                <td>#intranet-invoices.Surcharge# &nbsp;</td>
                <td><input type=text name=surcharge_text value="@surcharge_text@"> </td>
                <td><input type=text name=surcharge_perc value="@surcharge_perc@" size=4> % &nbsp;</td>
              </tr>
            </table>
          </td>
        </tr>
</if>
        <tr>
          <td> 
          </td>
          <td colspan=@vat_colspan@ align=right> 
            <table border=0 cellspacing=1 cellpadding=0>
              <tr> 
                <td>#intranet-invoices.VATnbsp#</td>
                <td><input type=text name=vat value="@vat@" size=4> % &nbsp;</td>
              </tr>
<if @tax_enabled_p@>
              <tr> 
                <td>#intranet-invoices.TAXnbsp#</td>
                <td><input type=text name=tax value="@tax@" size=4> % &nbsp;</td>
              </tr>
</if>
<else>
              <input type=hidden name=tax value="@tax@">
</else>
            </table>
          </td>
        </tr>
        <tr> 
          <td>&nbsp; </td>
          <td colspan=6 align=right> 
              <input type=submit name=submit value="@button_text@">
          </td>
        </tr>
 
        </table>
      </td>
    </tr>
  </table>
 
 
</td></tr>
</table>
 
</form>
 
</div>
 
 
<div>
 
 
<if @show_dynfield_tab_p@ eq 1>
	<formtemplate id="invoices_dynfield"></formtemplate>
	<input type="button" id="btn_save_dynfields" name="btn_save_dynfields" value="Save"> 
 
	<script type="text/javascript">
	var sUrl = ""
	var postData = ""
 
	function saveDynfields(p_oEvent) {
		sUrl = "/intranet-rest/im_invoice/@invoice_id;noquote@"
		postData = "<?xml version='1.0'?><im_invoice>" + @ajax_post_data;noquote@ + "</im_invoice>";
		var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); 
	}
 
	// Button  
	var oPushButton2 = new YAHOO.widget.Button("btn_save_dynfields"); 
	oPushButton2.on("click", saveDynfields); 
 
	var handleSuccess = function(o){
		alert("Successfully saved");
		window.location = "/intranet-invoices/view?invoice_id=" + @invoice_id;noquote@;
	}
 
	var handleFailure = function(o){
		alert("Failure transmitting data, please review data")
	}	
 
 
	var callback =
	{
	  success:handleSuccess,
	  failure: handleFailure,
	  argument: ['foo','bar']
	};
 
	</script>
 
	</div>
	</div>
</if>
 
</div>
</div>