diff options
author | ivan <ivan> | 2008-09-08 02:47:22 +0000 |
---|---|---|
committer | ivan <ivan> | 2008-09-08 02:47:22 +0000 |
commit | b96629eb08231f78f334f78c0bd6c277c60844fa (patch) | |
tree | ace9813a42e1072a9f641c0dfad3036d22740996 | |
parent | 2c90d42ca355e77804fbd0d4fe158dcdc38392c6 (diff) |
add package invoice details & comments, RT#3810
-rw-r--r-- | FS/FS.pm | 2 | ||||
-rw-r--r-- | FS/FS/AccessRight.pm | 2 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 16 | ||||
-rw-r--r-- | FS/FS/cust_main.pm | 171 | ||||
-rw-r--r-- | FS/FS/cust_pkg.pm | 72 | ||||
-rw-r--r-- | FS/FS/cust_pkg_detail.pm | 140 | ||||
-rw-r--r-- | FS/MANIFEST | 2 | ||||
-rw-r--r-- | FS/t/cust_pkg_detail.t | 5 | ||||
-rw-r--r-- | httemplate/edit/cust_pkg_detail.html | 142 | ||||
-rw-r--r-- | httemplate/edit/process/cust_pkg_detail.html | 59 | ||||
-rw-r--r-- | httemplate/pref/pref-process.html | 2 | ||||
-rw-r--r-- | httemplate/pref/pref.html | 24 | ||||
-rwxr-xr-x | httemplate/view/cust_main/packages.html | 140 |
13 files changed, 678 insertions, 99 deletions
@@ -210,6 +210,8 @@ L<FS::cust_pkg> - Customer package class L<FS::cust_pkg_option> - Customer package option class +L<FS::cust_pkg_detail> - Customer package details class + L<FS::reason_type> - Reason type class L<FS::reason> - Reason class diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm index db2a31f4d..9ef35249c 100644 --- a/FS/FS/AccessRight.pm +++ b/FS/FS/AccessRight.pm @@ -121,6 +121,8 @@ tie my %rights, 'Tie::IxHash', 'Cancel customer package later', 'Add on-the-fly cancel reason', #NEW 'Add on-the-fly suspend reason', #NEW + 'Edit customer package invoice details', #NEW + 'Edit customer package comments', #NEW ], ### diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 94a56248b..ed535f7de 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -500,9 +500,6 @@ sub tables_hashref { 'quantity', 'int', 'NULL', '', '', '', 'unitsetup', @money_typen, '', '', 'unitrecur', @money_typen, '', '', - 'duplicate', 'char', 'NULL', 1, '', '', - 'post_total', 'char', 'NULL', 1, '', '', - 'type', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'billpkgnum', 'unique' => [], @@ -945,6 +942,19 @@ sub tables_hashref { 'index' => [ [ 'pkgnum' ], [ 'optionname' ] ], }, + 'cust_pkg_detail' => { + 'columns' => [ + 'pkgdetailnum', 'serial', '', '', '', '', + 'pkgnum', 'int', '', '', '', '', + 'detail', 'varchar', '', $char_d, '', '', + 'detailtype', 'char', '', 1, '', '', # "I"nvoice or "C"omment + 'weight', 'int', '', '', '', '', + ], + 'primary_key' => 'pkgdetailnum', + 'unique' => [], + 'index' => [ [ 'pkgnum', 'detailtype' ] ], + }, + 'cust_pkg_reason' => { 'columns' => [ 'num', 'serial', '', '', '', '', diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index e9e21b80b..3f974b607 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -64,7 +64,7 @@ $realtime_bop_decline_quiet = 0; # 1 is mostly method/subroutine entry and options # 2 traces progress of some operations # 3 is even more information including possibly sensitive data -$DEBUG = 0; +$DEBUG = 2; $me = '[FS::cust_main]'; $import = 0; @@ -2047,7 +2047,6 @@ Used in conjunction with the I<time> option, this option specifies the date of f sub bill { my( $self, %options ) = @_; return '' if $self->payby eq 'COMP'; - local $DEBUG = 1; warn "$me bill customer ". $self->custnum. "\n" if $DEBUG; @@ -2442,6 +2441,13 @@ sub _make_lines { warn " charges (setup=$setup, recur=$recur); adding line items\n" if $DEBUG > 1; + my @cust_pkg_detail = map { $_->detail } $cust_pkg->cust_pkg_detail('I'); + if ( $DEBUG > 1 ) { + warn " adding customer package invoice detail: $_\n" + foreach @cust_pkg_detail; + } + push @details, @cust_pkg_detail; + my $cust_bill_pkg = new FS::cust_bill_pkg { 'pkgnum' => $cust_pkg->pkgnum, 'setup' => $setup, @@ -2463,13 +2469,11 @@ sub _make_lines { # handle taxes ### - my $err_or_cust_bill_pkg = + my $error = $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg, $cust_pkg); + return $error if $error; - return $err_or_cust_bill_pkg - unless ( ref($err_or_cust_bill_pkg) ); - - push @$cust_bill_pkgs, @$err_or_cust_bill_pkg; + push @$cust_bill_pkgs, $cust_bill_pkg; } #if $setup != 0 || $recur != 0 @@ -2518,7 +2522,7 @@ sub _handle_taxes { $taxes{''} = $err_or_ref; } - }elsif ( $self->tax !~ /Y/i && $self->payby ne 'COMP' ) { + } elsif ( $self->tax !~ /Y/i && $self->payby ne 'COMP' ) { my %taxhash = map { $_ => $self->get("$prefix$_") } qw( state county country ); @@ -2556,83 +2560,88 @@ sub _handle_taxes { } #if $conf->exists('enable_taxproducts') ... my $section = $cust_pkg->part_pkg->option('usage_section', 'Hush!') - if $cust_pkg->part_pkg->option('separate_usage'); + if $cust_pkg->part_pkg->option('separate_usage', 'Hush!' ); my $want_duplicate = $cust_pkg->part_pkg->option('summarize_usage', 'Hush!') && $cust_pkg->part_pkg->option('usage_section', 'Hush!'); - # XXX this mostly goes away with cust_bill_pkg refactor - - $cust_bill_pkg{setup} = $cust_bill_pkg if $cust_bill_pkg->setup; - $cust_bill_pkg{recur} = $cust_bill_pkg if $cust_bill_pkg->recur; - - #split setup and recur - if ($cust_bill_pkg->setup && $cust_bill_pkg->recur) { - my $cust_bill_pkg_recur = new FS::cust_bill_pkg { $cust_bill_pkg->hash }; - $cust_bill_pkg->set('details', []); - $cust_bill_pkg->recur(0); - $cust_bill_pkg->unitrecur(0); - $cust_bill_pkg->type(''); - $cust_bill_pkg_recur->setup(0); - $cust_bill_pkg_recur->unitsetup(0); - $cust_bill_pkg{recur} = $cust_bill_pkg_recur; - } - - #split usage from recur - my $usage = sprintf( "%.2f", $cust_bill_pkg{recur}->usage ); - warn "usage is $usage\n" if $DEBUG; - if ($usage) { - my $cust_bill_pkg_usage = - new FS::cust_bill_pkg { $cust_bill_pkg{recur}->hash }; - $cust_bill_pkg_usage->recur( $usage ); - $cust_bill_pkg_usage->type( 'U' ); - $cust_bill_pkg_usage->duplicate( $want_duplicate ? 'Y' : '' ); - $cust_bill_pkg_usage->section( $section ); - $cust_bill_pkg_usage->post_total( $want_duplicate ? 'Y' : '' ); - my $recur = sprintf( "%.2f", $cust_bill_pkg{recur}->recur - $usage ); - $cust_bill_pkg{recur}->recur( $recur ); - $cust_bill_pkg{recur}->type( '' ); - $cust_bill_pkg{recur}->set('details', []); - $cust_bill_pkg{''} = $cust_bill_pkg_usage; - } - - #subdivide usage by usage_class - if (exists($cust_bill_pkg{''})) { - foreach my $class (grep {$_ && $_ ne 'setup' && $_ ne 'recur' } @classes) { - my $usage = sprintf( "%.2f", $cust_bill_pkg{''}->usage($class) ); - my $cust_bill_pkg_usage = - new FS::cust_bill_pkg { $cust_bill_pkg{''}->hash }; - $cust_bill_pkg_usage->recur( $usage ); - $cust_bill_pkg_usage->set('details', []); - my $classless = sprintf( "%.2f", $cust_bill_pkg{''}->recur - $usage ); - $cust_bill_pkg{''}->recur( $classless ); - $cust_bill_pkg{$class} = $cust_bill_pkg_usage; - } - delete $cust_bill_pkg{''} unless $cust_bill_pkg{''}->recur; - } - - foreach my $key (keys %cust_bill_pkg) { - my @taxes = @{ $taxes{$key} }; - my $cust_bill_pkg = $cust_bill_pkg{$key}; - - foreach my $tax ( @taxes ) { - my $taxname = ref( $tax ). ' '. $tax->taxnum; - if ( exists( $taxlisthash->{ $taxname } ) ) { - push @{ $taxlisthash->{ $taxname } }, $cust_bill_pkg; - }else{ - $taxlisthash->{ $taxname } = [ $tax, $cust_bill_pkg ]; - } - } - } - - # sort setup,recur,'', and the rest numeric && return - my @result = map { $cust_bill_pkg{$_} } - sort { my $ad = ($a=~/^\d+$/); my $bd = ($b=~/^\d+$/); - ( $ad cmp $bd ) || ( $ad ? $a<=>$b : $b cmp $a ) - } - keys %cust_bill_pkg; - - \@result; +#BUNK. DO NOT CREATE DUPLICATE cust_bill_pkg!!!!!!!!!!!! +# +# # XXX this mostly goes away with cust_bill_pkg refactor +# +# $cust_bill_pkg{setup} = $cust_bill_pkg if $cust_bill_pkg->setup; +# $cust_bill_pkg{recur} = $cust_bill_pkg if $cust_bill_pkg->recur; +# +# +# #split setup and recur +# if ($cust_bill_pkg->setup && $cust_bill_pkg->recur) { +# my $cust_bill_pkg_recur = new FS::cust_bill_pkg { $cust_bill_pkg->hash }; +# $cust_bill_pkg_recur->details($cust_bill_pkg-> +# $cust_bill_pkg_recur->setup(0); +# $cust_bill_pkg_recur->unitsetup(0); +# $cust_bill_pkg{recur} = $cust_bill_pkg_recur; +# +# $cust_bill_pkg->set('details', []); +# $cust_bill_pkg->recur(0); +# $cust_bill_pkg->unitrecur(0); +# $cust_bill_pkg->type(''); +# } +# +# #split usage from recur +# my $usage = sprintf( "%.2f", $cust_bill_pkg{recur}->usage ); +# warn "usage is $usage\n" if $DEBUG; +# if ($usage) { +# my $cust_bill_pkg_usage = +# new FS::cust_bill_pkg { $cust_bill_pkg{recur}->hash }; +# $cust_bill_pkg_usage->recur( $usage ); +# $cust_bill_pkg_usage->type( 'U' ); +# $cust_bill_pkg_usage->duplicate( $want_duplicate ? 'Y' : '' ); +# $cust_bill_pkg_usage->section( $section ); +# $cust_bill_pkg_usage->post_total( $want_duplicate ? 'Y' : '' ); +# my $recur = sprintf( "%.2f", $cust_bill_pkg{recur}->recur - $usage ); +# $cust_bill_pkg{recur}->recur( $recur ); +# $cust_bill_pkg{recur}->type( '' ); +# $cust_bill_pkg{recur}->set('details', []); +# $cust_bill_pkg{''} = $cust_bill_pkg_usage; +# } +# +# #subdivide usage by usage_class +# if (exists($cust_bill_pkg{''})) { +# foreach my $class (grep {$_ && $_ ne 'setup' && $_ ne 'recur' } @classes) { +# my $usage = sprintf( "%.2f", $cust_bill_pkg{''}->usage($class) ); +# my $cust_bill_pkg_usage = +# new FS::cust_bill_pkg { $cust_bill_pkg{''}->hash }; +# $cust_bill_pkg_usage->recur( $usage ); +# $cust_bill_pkg_usage->set('details', []); +# my $classless = sprintf( "%.2f", $cust_bill_pkg{''}->recur - $usage ); +# $cust_bill_pkg{''}->recur( $classless ); +# $cust_bill_pkg{$class} = $cust_bill_pkg_usage; +# } +# delete $cust_bill_pkg{''} unless $cust_bill_pkg{''}->recur; +# } +# +# foreach my $key (keys %cust_bill_pkg) { +# my @taxes = @{ $taxes{$key} }; +# my $cust_bill_pkg = $cust_bill_pkg{$key}; +# +# foreach my $tax ( @taxes ) { +# my $taxname = ref( $tax ). ' '. $tax->taxnum; +# if ( exists( $taxlisthash->{ $taxname } ) ) { +# push @{ $taxlisthash->{ $taxname } }, $cust_bill_pkg; +# }else{ +# $taxlisthash->{ $taxname } = [ $tax, $cust_bill_pkg ]; +# } +# } +# } +# +# # sort setup,recur,'', and the rest numeric && return +# my @result = map { $cust_bill_pkg{$_} } +# sort { my $ad = ($a=~/^\d+$/); my $bd = ($b=~/^\d+$/); +# ( $ad cmp $bd ) || ( $ad ? $a<=>$b : $b cmp $a ) +# } +# keys %cust_bill_pkg; +# +# \@result; } sub _gather_taxes { diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 4f3579eba..b0e27f28c 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -16,6 +16,7 @@ use FS::cust_main; use FS::type_pkgs; use FS::pkg_svc; use FS::cust_bill_pkg; +use FS::cust_pkg_detail; use FS::cust_event; use FS::h_cust_svc; use FS::reg_code; @@ -1089,6 +1090,77 @@ sub cust_bill_pkg { qsearch( 'cust_bill_pkg', { 'pkgnum' => $self->pkgnum } ); } +=item cust_pkg_detail [ DETAILTYPE ] + +Returns any customer package details for this package (see +L<FS::cust_pkg_detail>). + +DETAILTYPE can be set to "I" for invoice details or "C" for comments. + +=cut + +sub cust_pkg_detail { + my $self = shift; + my %hash = ( 'pkgnum' => $self->pkgnum ); + $hash{detailtype} = shift if @_; + qsearch({ + 'table' => 'cust_pkg_detail', + 'hashref' => \%hash, + 'order_by' => 'ORDER BY weight, pkgdetailnum', + }); +} + +=item set_cust_pkg_detail DETAILTYPE [ DETAIL, DETAIL, ... ] + +Sets customer package details for this package (see L<FS::cust_pkg_detail>). + +DETAILTYPE can be set to "I" for invoice details or "C" for comments. + +If there is an error, returns the error, otherwise returns false. + +=cut + +sub set_cust_pkg_detail { + my( $self, $detailtype, @details ) = @_; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + foreach my $current ( $self->cust_pkg_detail($detailtype) ) { + my $error = $current->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "error removing old detail: $error"; + } + } + + foreach my $detail ( @details ) { + my $cust_pkg_detail = new FS::cust_pkg_detail { + 'pkgnum' => $self->pkgnum, + 'detailtype' => $detailtype, + 'detail' => $detail, + }; + my $error = $cust_pkg_detail->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "error adding new detail: $error"; + } + + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + =item cust_event Returns the new-style customer billing events (see L<FS::cust_event>) for this invoice. diff --git a/FS/FS/cust_pkg_detail.pm b/FS/FS/cust_pkg_detail.pm new file mode 100644 index 000000000..e2d8987bd --- /dev/null +++ b/FS/FS/cust_pkg_detail.pm @@ -0,0 +1,140 @@ +package FS::cust_pkg_detail; + +use strict; +use vars qw( @ISA ); +use FS::Record; # qw( qsearch qsearchs ); + +@ISA = qw(FS::Record); + +=head1 NAME + +FS::cust_pkg_detail - Object methods for cust_pkg_detail records + +=head1 SYNOPSIS + + use FS::cust_pkg_detail; + + $record = new FS::cust_pkg_detail \%hash; + $record = new FS::cust_pkg_detail { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_pkg_detail object represents additional customer package details. +FS::cust_pkg_detail inherits from FS::Record. The following fields are +currently supported: + +=over 4 + +=item pkgdetailnum + +primary key + +=item pkgnum + +pkgnum (see L<FS::cust_pkg>) + +=item detail + +detail + +=item detailtype + +"I" for Invoice details or "C" for comments + +=item weight + +Optional display weight + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new record. To add the record to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I<hash> method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'cust_pkg_detail'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('pkgdetailnum') + || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum') + || $self->ut_text('detail') + || $self->ut_enum('detailtype', [ 'I', 'C' ] ) + || $self->ut_numbern('weight') + ; + return $error if $error; + + $self->weight(0) unless $self->weight; + + $self->SUPER::check; +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L<FS::cust_pkg>, L<FS::Record>, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/MANIFEST b/FS/MANIFEST index a6fe21911..b1f201d0a 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -422,3 +422,5 @@ FS/cust_svc_option.pm t/cust_svc_option.t FS/usage_class.pm t/usage_class.t +FS/cust_pkg_detail.pm +t/cust_pkg_detail.t diff --git a/FS/t/cust_pkg_detail.t b/FS/t/cust_pkg_detail.t new file mode 100644 index 000000000..15dec0014 --- /dev/null +++ b/FS/t/cust_pkg_detail.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::cust_pkg_detail; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html new file mode 100644 index 000000000..009ed5c6e --- /dev/null +++ b/httemplate/edit/cust_pkg_detail.html @@ -0,0 +1,142 @@ +<% include("/elements/header-popup.html", $title, '', + ( $cgi->param('error') ? '' : 'onload="addRow()"' ), + ) +%> + +%# <% include('/elements/error.html') %> + +<FORM ACTION="process/cust_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="detailtype" VALUE="<% $detailtype %>"> + +<TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc"> + +% if ( $curuser->option('show_pkgnum') ) { + + <TR> + <TD ALIGN="right">Package #</TD> + <TD BGCOLOR="#ffffff"><% $pkgnum %></TD> + </TR> + +% } + + <TR> + <TD ALIGN="right">Package</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> + </TR> + + <TR> + <TD ALIGN="right">Comment</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> + </TR> + + <TR> + <TD ALIGN="right">Status</TD> + <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_pkg->statuscolor %>"><B><% ucfirst($cust_pkg->status) %></B></FONT></TD> + </TR> + + <TR> + <TD COLSPAN=2><% ucfirst($name{$detailtype}) %>: </TD> + </TR> + +% my $row = 0; +% for ( @details ) { + + <TR> + <TD></TD> + <TD> + <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" > + </TD> + </TR> + +% } + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $title %>"> + +</FORM> + +<SCRIPT TYPE="text/javascript"> + + var rownum = <% $row %>; + + function possiblyAddRow() { + if ( ( rownum - this.getAttribute('rownum') ) == 1 ) { + addRow(); + } + } + + function addRow() { + + var table = document.getElementById('DetailTable'); + var tablebody = table.getElementsByTagName('tbody').item(0); + + var row = document.createElement('TR'); + + var empty_cell = document.createElement('TD'); + row.appendChild(empty_cell); + + var detail_cell = document.createElement('TD'); + + var detail_input = document.createElement('INPUT'); + detail_input.setAttribute('name', 'detail'+rownum); + detail_input.setAttribute('id', 'detail'+rownum); + detail_input.setAttribute('size', 60); + detail_input.setAttribute('maxLength', 65); + detail_input.setAttribute('rownum', rownum); + detail_input.onkeyup = possiblyAddRow; + detail_cell.appendChild(detail_input); + + row.appendChild(detail_cell); + + tablebody.appendChild(row); + + rownum++; + + } + +</SCRIPT> + +</BODY> +</HTML> +<%init> + +my %access_right = ( + 'I' => 'Edit customer package invoice details', + 'C' => 'Edit customer package comments', +); + +my %name = ( + 'I' => 'invoice details', + 'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" + unless $curuser->access_right($right); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $cust_pkg = qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + +my $part_pkg = $cust_pkg->part_pkg; + +my @details = $cust_pkg->cust_pkg_detail($detailtype); + +my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). $name{$detailtype}; + +</%init> diff --git a/httemplate/edit/process/cust_pkg_detail.html b/httemplate/edit/process/cust_pkg_detail.html new file mode 100644 index 000000000..132ff63c5 --- /dev/null +++ b/httemplate/edit/process/cust_pkg_detail.html @@ -0,0 +1,59 @@ +% if ( $error ) { +<% header('Error') %> +<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR> +<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER> +</BODY></HTML> +% } else { +<% header($action) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +my %access_right = ( + 'I' => 'Edit customer package invoice details', + 'C' => 'Edit customer package comments', +); + +my %name = ( + 'I' => 'invoice details', + 'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" + unless $curuser->access_right($right); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $cust_pkg = qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + + +my @orig_details = $cust_pkg->cust_pkg_detail($detailtype); + +my $action = ucfirst($name{$detailtype}). + ( scalar(@orig_details) ? ' changed ' : ' added ' ); + +my $param = $cgi->Vars; +my @details = (); +for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) { + push @details, $param->{"detail$row"} + if $param->{"detail$row"} =~ /\S/; +} + +my $error = $cust_pkg->set_cust_pkg_detail($detailtype, @details); + +</%init> diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index 25f30e9a9..09eb71a7e 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -30,7 +30,7 @@ % my %param = $access_user->options; % % #XXX autogen -% my @paramlist = qw( menu_position +% my @paramlist = qw( menu_position show_pkgnum % email_address % vonage-fromnumber vonage-username vonage-password % height width availHeight availWidth colorDepth diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index de5bd8270..4007bb9f1 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -57,22 +57,34 @@ Email Address <BR> +Development +<% ntable("#cccccc",2) %> + + <TR> + <TH>Show internal package numbers: </TH> + <TD><INPUT TYPE="checkbox" NAME="show_pkgnum" VALUE="1" <% $curuser->option('show_pkgnum') ? 'CHECKED' : '' %>></TD> + </TR> + +</TABLE> +<BR> + + Vonage integration (see <a href="https://secure.click2callu.com/">Click2Call</a>) <% ntable("#cccccc",2) %> <TR> <TH ALIGN="right">Vonage phone number</TH> - <TD><INPUT TYPE="text" NAME="vonage-fromnumber" VALUE="<% $FS::CurrentUser::CurrentUser->option('vonage-fromnumber') %>"></TD> + <TD><INPUT TYPE="text" NAME="vonage-fromnumber" VALUE="<% $curuser->option('vonage-fromnumber') %>"></TD> </TR> <TR> <TH ALIGN="right">Vonage username</TH> - <TD><INPUT TYPE="text" NAME="vonage-username" VALUE="<% $FS::CurrentUser::CurrentUser->option('vonage-username') %>"></TD> + <TD><INPUT TYPE="text" NAME="vonage-username" VALUE="<% $curuser->option('vonage-username') %>"></TD> </TR> <TR> <TH ALIGN="right">Vonage password</TH> - <TD><INPUT TYPE="password" NAME="vonage-password" VALUE="<% $FS::CurrentUser::CurrentUser->option('vonage-password') %>"></TD> + <TD><INPUT TYPE="password" NAME="vonage-password" VALUE="<% $curuser->option('vonage-password') %>"></TD> </TR> </TABLE> @@ -91,11 +103,13 @@ Vonage integration (see <a href="https://secure.click2callu.com/">Click2Call</a> <% include('/elements/footer.html') %> <%init> +my $curuser = $FS::CurrentUser::CurrentUser; + # XSS via your own preferences? seems unlikely, but nice try anyway... -( $FS::CurrentUser::CurrentUser->option('menu_position') || 'left' ) +( $curuser->option('menu_position') || 'left' ) =~ /^(\w+)$/ or die "illegal menu_position"; my $menu_position = $1; -( $FS::CurrentUser::CurrentUser->option('email_address') ) +( $curuser->option('email_address') ) =~ /^([,\w\@.]*)$/ or die "illegal email_address"; #too late my $email_address = $1; diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 9d5f993cd..ad5595c1a 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -85,7 +85,7 @@ function taxoverridequickchargemagic() { 'label' => 'One-time charge', 'actionlabel' => 'One-time charge', 'color' => '#333399', - 'width' => 545, + 'width' => 763, }) %> % } @@ -149,17 +149,32 @@ Current packages <!--pkgnum: <% $cust_pkg->pkgnum %>--> <TR> - <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> - - <A NAME="cust_pkg<% $cust_pkg->pkgnum %>" ID="cust_pkg<% $cust_pkg->pkgnum %>"><% $cust_pkg->pkgnum %></A>: - <% $part_pkg->pkg %> - <% $part_pkg->comment %> - <BR> + <TD CLASS="inv" BGCOLOR="<% $bgcolor %>"> + <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%"> + <TR> + <TD COLSPAN=2> + <A NAME="cust_pkg<% $cust_pkg->pkgnum %>" + ID ="cust_pkg<% $cust_pkg->pkgnum %>" + ><% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><% $part_pkg->pkg %></A> + - + <% $part_pkg->comment %> + </TD> + </TR> % if ( $cust_pkg->quantity > 1 ) { - Quantity: <B><% $cust_pkg->quantity %></B><BR> + <TR> + <TD COLSPAN=2> + Quantity: + <B><% $cust_pkg->quantity %></B> + </TD> + </TR> % } - <FONT SIZE=-1> + <TR> + <TD COLSPAN=2> + + <FONT SIZE=-1> + % unless ( $cust_pkg->get('cancel') ) { % my $br = 0; % if ( $curuser->access_right('Change customer package') ) { $br=1; @@ -185,7 +200,114 @@ Current packages ( <%pkg_event_link($cust_pkg)%> ) % } - </FONT> + </FONT> + + </TD> + </TR> + +% my $editi = $curuser->access_right('Edit customer package invoice details'); +% my $editc = $curuser->access_right('Edit customer package comments'); +% +% if ( $cust_pkg->cust_pkg_detail('I') || $cust_pkg->cust_pkg_detail('C') +% || $editi || $editc ) { +% +% my $editlink = $p. 'edit/cust_pkg_detail?pkgnum='. $cust_pkg->pkgnum. +% ';detailtype='; + + <TR> + +% if ( $cust_pkg->cust_pkg_detail('I') ) { + <TD VALIGN="top"> + <% include('/elements/table-grid.html') %> + <TR> + <TH BGCOLOR="#dddddd" STYLE="border-bottom: dashed 1px black; padding-bottom: 1px"> + <FONT SIZE="-1"> + Invoice details +% if ( $editi && ! $cust_pkg->get('cancel') ) { + (<% include('/elements/popup_link.html', { + 'action' => $editlink. 'I', + 'label' => 'edit', + 'actionlabel' => 'Edit invoice details', + 'color' => '#333399', + 'width' => 763, + }) + %>) +% } + </FONT> + </TH> + </TR> +% foreach my $cust_pkg_detail ( $cust_pkg->cust_pkg_detail('I') ) { + <TR> + <TD><FONT SIZE="-1"> - <% $cust_pkg_detail->detail |h %></FONT></TD> + </TR> +% } + </TABLE> + </TD> +% } else { + <TD> +% if ( $editi && ! $cust_pkg->get('cancel') ) { + <FONT SIZE="-1"> + ( <% include('/elements/popup_link.html', { + 'action' => $editlink. 'I', + 'label' => 'Add invoice details', + 'actionlabel' => 'Add invoice details', + 'color' => '#333399', + 'width' => 763, + }) + %> ) + </FONT> +% } + </TD> +% } + +% if ( $cust_pkg->cust_pkg_detail('C') ) { + <TD VALIGN="top"> + <% include('/elements/table-grid.html') %> + <TR> + <TH BGCOLOR="#dddddd" STYLE="border-bottom: dashed 1px black; padding-bottom: 1px"> + <FONT SIZE="-1"> + Comments +% if ( $editc ) { + (<% include('/elements/popup_link.html', { + 'action' => $editlink. 'C', + 'label' => 'edit', + 'actionlabel' => 'Edit comments', + 'color' => '#333399', + 'width' => 763, + }) + %>) +% } + </FONT> + </TH> + </TR> +% foreach my $cust_pkg_detail ( $cust_pkg->cust_pkg_detail('C') ) { + <TR> + <TD><FONT SIZE="-1"> - <% $cust_pkg_detail->detail |h %></FONT></TD> + </TR> +% } + </TABLE> + </TD> +% } else { + <TD> +% if ( $editc ) { + <FONT SIZE="-1"> + ( <% include('/elements/popup_link.html', { + 'action' => $editlink. 'C', + 'label' => 'Add comments', + 'actionlabel' => 'Add comments', + 'color' => '#333399', + 'width' => 763, + }) + %> ) + </FONT> +% } + </TD> +% } + + </TR> +% } + + </TABLE> </TD> |