add package invoice details & comments, RT#3810
authorivan <ivan>
Mon, 8 Sep 2008 02:47:22 +0000 (02:47 +0000)
committerivan <ivan>
Mon, 8 Sep 2008 02:47:22 +0000 (02:47 +0000)
13 files changed:
FS/FS.pm
FS/FS/AccessRight.pm
FS/FS/Schema.pm
FS/FS/cust_main.pm
FS/FS/cust_pkg.pm
FS/FS/cust_pkg_detail.pm [new file with mode: 0644]
FS/MANIFEST
FS/t/cust_pkg_detail.t [new file with mode: 0644]
httemplate/edit/cust_pkg_detail.html [new file with mode: 0644]
httemplate/edit/process/cust_pkg_detail.html [new file with mode: 0644]
httemplate/pref/pref-process.html
httemplate/pref/pref.html
httemplate/view/cust_main/packages.html

index a2f72bc..b0ff811 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -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
index db2a31f..9ef3524 100644 (file)
@@ -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
   ],
   
   ###
index 94a5624..ed535f7 100644 (file)
@@ -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',    '',   '', '', '', 
index e9e21b8..3f974b6 100644 (file)
@@ -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 {
index 4f3579e..b0e27f2 100644 (file)
@@ -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 (file)
index 0000000..e2d8987
--- /dev/null
@@ -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;
+
index a6fe219..b1f201d 100644 (file)
@@ -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 (file)
index 0000000..15dec00
--- /dev/null
@@ -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 (file)
index 0000000..009ed5c
--- /dev/null
@@ -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 (file)
index 0000000..132ff63
--- /dev/null
@@ -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>
index 25f30e9..09eb71a 100644 (file)
@@ -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
index de5bd82..4007bb9 100644 (file)
@@ -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;
 
index 9d5f993..ad5595c 100755 (executable)
@@ -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 ) {
-        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quantity: <B><% $cust_pkg->quantity %></B><BR>
+      <TR>
+        <TD COLSPAN=2>
+          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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
     (&nbsp;<%pkg_event_link($cust_pkg)%>&nbsp;)
 % }
 
-    </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">&nbsp;-&nbsp;<% $cust_pkg_detail->detail |h %></FONT></TD>
+                </TR>
+%             }
+            </TABLE>
+          </TD>
+%       } else {
+          <TD>
+%           if ( $editi && ! $cust_pkg->get('cancel') ) {
+              <FONT SIZE="-1">
+                (&nbsp;<% include('/elements/popup_link.html', { 
+                               'action'      => $editlink. 'I',
+                               'label'       => 'Add&nbsp;invoice&nbsp;details',
+                               'actionlabel' => 'Add invoice details',
+                               'color'       => '#333399',
+                               'width'       => 763,
+                            })
+                  %>&nbsp;)
+              </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">&nbsp;-&nbsp;<% $cust_pkg_detail->detail |h %></FONT></TD>
+                </TR>
+%             }
+            </TABLE>
+          </TD>
+%       } else {
+          <TD>
+%           if ( $editc ) {
+              <FONT SIZE="-1">
+                (&nbsp;<% include('/elements/popup_link.html', { 
+                               'action'      => $editlink. 'C',
+                               'label'       => 'Add&nbsp;comments',
+                               'actionlabel' => 'Add comments',
+                               'color'       => '#333399',
+                               'width'       => 763,
+                            })
+                  %>&nbsp;)
+              </FONT>
+%           }
+          </TD>
+%       }
+
+      </TR>
+%   }
+
+    </TABLE>
 
   </TD>