option to credit unused time on suspension as part of suspend reason, #31702
authorMark Wells <mark@freeside.biz>
Wed, 26 Nov 2014 03:39:23 +0000 (19:39 -0800)
committerMark Wells <mark@freeside.biz>
Wed, 26 Nov 2014 03:39:23 +0000 (19:39 -0800)
18 files changed:
FS/FS/Schema.pm
FS/FS/cust_credit.pm
FS/FS/cust_pkg.pm
FS/FS/reason.pm
httemplate/edit/credit-cust_bill_pkg.html
httemplate/edit/cust_credit.cgi
httemplate/edit/process/credit-cust_bill_pkg.html
httemplate/edit/process/cust_credit.cgi
httemplate/elements/tr-select-reason.html
httemplate/misc/cancel_cust.html
httemplate/misc/cancel_pkg.html
httemplate/misc/cust_main-cancel.cgi
httemplate/misc/cust_main-suspend.cgi
httemplate/misc/process/cancel_pkg.html
httemplate/misc/process/elements/reason [new file with mode: 0644]
httemplate/misc/suspend_cust.html
httemplate/view/cust_main.cgi
httemplate/view/cust_main/packages/status.html

index 396c866..715a603 100644 (file)
@@ -5881,6 +5881,7 @@ sub tables_hashref {
         'disabled',      'char',    'NULL', 1, '', '', 
         'unsuspend_pkgpart', 'int',  'NULL', '', '', '',
         'unsuspend_hold','char',    'NULL', 1, '', '',
         'disabled',      'char',    'NULL', 1, '', '', 
         'unsuspend_pkgpart', 'int',  'NULL', '', '', '',
         'unsuspend_hold','char',    'NULL', 1, '', '',
+        'unused_credit', 'char',    'NULL', 1, '', '',
       ],
       'primary_key'  => 'reasonnum',
       'unique'       => [],
       ],
       'primary_key'  => 'reasonnum',
       'unique'       => [],
index 156ba5f..212be7a 100644 (file)
@@ -679,11 +679,9 @@ Example:
     'apply'             => 1, #0 leaves the credit unapplied
 
     #the credit
     'apply'             => 1, #0 leaves the credit unapplied
 
     #the credit
-    'newreasonnum'      => scalar($cgi->param('newreasonnum')),
-    'newreasonnum_type' => scalar($cgi->param('newreasonnumT')),
     map { $_ => scalar($cgi->param($_)) }
       #fields('cust_credit')  
     map { $_ => scalar($cgi->param($_)) }
       #fields('cust_credit')  
-      qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
+      qw( custnum _date amount reasonnum addlinfo ), #pkgnum eventnum
 
   );
 
 
   );
 
@@ -725,26 +723,11 @@ sub credit_lineitems {
   #});
 
   my $error = '';
   #});
 
   my $error = '';
-  if ($arg{reasonnum} == -1) {
-
-    $error = 'Enter a new reason (or select an existing one)'
-      unless $arg{newreasonnum} !~ /^\s*$/;
-    my $reason = new FS::reason {
-                   'reason'      => $arg{newreasonnum},
-                   'reason_type' => $arg{newreasonnum_type},
-                 };
-    $error ||= $reason->insert;
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return "Error inserting reason: $error";
-    }
-    $arg{reasonnum} = $reason->reasonnum;
-  }
 
   my $cust_credit = new FS::cust_credit ( {
     map { $_ => $arg{$_} }
       #fields('cust_credit')
 
   my $cust_credit = new FS::cust_credit ( {
     map { $_ => $arg{$_} }
       #fields('cust_credit')
-      qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
+      qw( custnum _date amount reasonnum addlinfo ), #pkgnum eventnum
   } );
   $error = $cust_credit->insert;
   if ( $error ) {
   } );
   $error = $cust_credit->insert;
   if ( $error ) {
index e8e202e..a810f5a 100644 (file)
@@ -1207,7 +1207,7 @@ Available options are:
 
 =over 4
 
 
 =over 4
 
-=item reason - can be set to a cancellation reason (see L<FS:reason>), 
+=item reason - can be set to a cancellation reason (see L<FS:reason>),
 either a reasonnum of an existing reason, or passing a hashref will create 
 a new reason.  The hashref should have the following keys: 
 - typenum - Reason type (see L<FS::reason_type>
 either a reasonnum of an existing reason, or passing a hashref will create 
 a new reason.  The hashref should have the following keys: 
 - typenum - Reason type (see L<FS::reason_type>
@@ -1297,6 +1297,16 @@ sub suspend {
     }
   }
 
     }
   }
 
+  # if a reasonnum was passed, get the actual reason object so we can check
+  # unused_credit
+  # (passing a reason hashref is still allowed, but it can't be used with
+  # the fancy behavioral options.)
+
+  my $reason;
+  if ($options{'reason'} =~ /^\d+$/) {
+    $reason = FS::reason->by_key($options{'reason'});
+  }
+
   my %hash = $self->hash;
   if ( $date ) {
     $hash{'adjourn'} = $date;
   my %hash = $self->hash;
   if ( $date ) {
     $hash{'adjourn'} = $date;
@@ -1321,9 +1331,15 @@ sub suspend {
     return $error;
   }
 
     return $error;
   }
 
-  unless ( $date ) {
+  unless ( $date ) { # then we are suspending now
+
     # credit remaining time if appropriate
     # credit remaining time if appropriate
-    if ( $self->part_pkg->option('unused_credit_suspend', 1) ) {
+    # (if required by the package def, or the suspend reason)
+    my $unused_credit = $self->part_pkg->option('unused_credit_suspend',1)
+                        || ( defined($reason) && $reason->unused_credit );
+
+    if ( $unused_credit ) {
+      warn "crediting unused time on pkg#".$self->pkgnum."\n" if $DEBUG;
       my $error = $self->credit_remaining('suspend', $suspend_time);
       if ($error) {
         $dbh->rollback if $oldAutoCommit;
       my $error = $self->credit_remaining('suspend', $suspend_time);
       if ($error) {
         $dbh->rollback if $oldAutoCommit;
@@ -3872,7 +3888,7 @@ sub insert_reason {
     $reasonnum = $reason->reasonnum;
 
   } else {
     $reasonnum = $reason->reasonnum;
 
   } else {
-    return "Unparsable reason: ". $options{'reason'};
+    return "Unparseable reason: ". $options{'reason'};
   }
 
   my $cust_pkg_reason =
   }
 
   my $cust_pkg_reason =
index e6b20db..f28989a 100644 (file)
@@ -56,6 +56,10 @@ suspensions but not others.
 whether to bill the unsuspend package immediately ('') or to wait until 
 the customer's next invoice ('Y').
 
 whether to bill the unsuspend package immediately ('') or to wait until 
 the customer's next invoice ('Y').
 
+=item unused_credit - 'Y' or ''. For suspension reasons only (for now).
+If enabled, the customer will be credited for their remaining time on 
+suspension.
+
 =back
 
 =head1 METHODS
 =back
 
 =head1 METHODS
@@ -109,7 +113,6 @@ sub check {
     || $self->ut_number('reason_type')
     || $self->ut_foreign_key('reason_type', 'reason_type', 'typenum')
     || $self->ut_text('reason')
     || $self->ut_number('reason_type')
     || $self->ut_foreign_key('reason_type', 'reason_type', 'typenum')
     || $self->ut_text('reason')
-    || $self->ut_flag('disabled')
   ;
   return $error if $error;
 
   ;
   return $error if $error;
 
@@ -117,11 +120,13 @@ sub check {
     $error = $self->ut_numbern('unsuspend_pkgpart')
           || $self->ut_foreign_keyn('unsuspend_pkgpart', 'part_pkg', 'pkgpart')
           || $self->ut_flag('unsuspend_hold')
     $error = $self->ut_numbern('unsuspend_pkgpart')
           || $self->ut_foreign_keyn('unsuspend_pkgpart', 'part_pkg', 'pkgpart')
           || $self->ut_flag('unsuspend_hold')
+          || $self->ut_flag('unused_credit')
     ;
     return $error if $error;
   } else {
     ;
     return $error if $error;
   } else {
-    $self->set('unsuspend_pkgpart' => '');
-    $self->set('unsuspend_hold'    => '');
+    foreach (qw(unsuspend_pkgpart unsuspend_hold unused_credit)) {
+      $self->set($_ => '');
+    }
   }
 
   $self->SUPER::check;
   }
 
   $self->SUPER::check;
@@ -178,8 +183,6 @@ sub new_or_existing {
 
 =head1 BUGS
 
 
 =head1 BUGS
 
-Here by termintes.  Don't use on wooden computers.
-
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
index 40faddc..85f7943 100644 (file)
@@ -80,9 +80,9 @@
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'R',
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'R',
-              #XXX reconcile both this and show_taxes wanteding to enable this
+              #XXX reconcile both this and show_taxes wanting to enable this
               'id'             => 'select_reason',
               'id'             => 'select_reason',
-              'control_button' => "document.getElementById('credit_button')",
+              'control_button' => 'credit_button',
               'cgi'            => $cgi,
 &>
 
               'cgi'            => $cgi,
 &>
 
index a3565f1..29801ef 100755 (executable)
@@ -24,7 +24,7 @@
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'R',
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'R',
-              'control_button' => "document.getElementById('confirm_credit_button')",
+              'control_button' => 'confirm_credit_button',
               'cgi'            => $cgi,
 &>
 
               'cgi'            => $cgi,
 &>
 
index 8e66368..75900bd 100644 (file)
@@ -27,19 +27,35 @@ foreach my $billpkgnum_setuprecur (@billpkgnum_setuprecurs) {
   push @amounts,     $amount;
 }
 
   push @amounts,     $amount;
 }
 
-my $error = FS::cust_credit->credit_lineitems(
+my $reasonnum = $cgi->param('reasonnum');
+$reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+$reasonnum = $1;
+
+my $error;
+if ($reasonnum == -1) {
+  my $new_reason = FS::reason->new({
+    map { $_ => scalar( $cgi->param("select_reason_new_$_") ) }
+    qw( reason_type reason )
+  });
+  $error = $new_reason->insert;
+  $reasonnum = $new_reason->reasonnum;
+}
+
+if ( !$reasonnum ) {
+  $error ||= 'Reason required'
+}
+
+$error ||= FS::cust_credit->credit_lineitems(
   #the lineitems to credit
   'billpkgnums'       => \@billpkgnums,
   'setuprecurs'       => \@setuprecurs,
   'amounts'           => \@amounts,
   'apply'             => ( $cgi->param('apply') eq 'yes' ),
   #the lineitems to credit
   'billpkgnums'       => \@billpkgnums,
   'setuprecurs'       => \@setuprecurs,
   'amounts'           => \@amounts,
   'apply'             => ( $cgi->param('apply') eq 'yes' ),
+  'reasonnum'         => $reasonnum,
 
 
-  #the credit
-  'newreasonnum'      => scalar($cgi->param('newreasonnum')),
-  'newreasonnum_type' => scalar($cgi->param('newreasonnumT')),
   map { $_ => scalar($cgi->param($_)) }
     #fields('cust_credit')  
   map { $_ => scalar($cgi->param($_)) }
     #fields('cust_credit')  
-    qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
+    qw( custnum _date amount addlinfo ), #pkgnum eventnum
 );
 
 </%init>
 );
 
 </%init>
index 245f31a..e442d7f 100755 (executable)
@@ -1,5 +1,4 @@
 %if ( $error ) {
 %if ( $error ) {
-%  $cgi->param('reasonnum', $reasonnum);
 %  $cgi->param('error', $error);
 %  $dbh->rollback if $oldAutoCommit;
 %  
 %  $cgi->param('error', $error);
 %  $dbh->rollback if $oldAutoCommit;
 %  
@@ -37,19 +36,11 @@ my $oldAutoCommit = $FS::UID::AutoCommit;
 local $FS::UID::AutoCommit = 0;
 my $dbh = dbh;
 
 local $FS::UID::AutoCommit = 0;
 my $dbh = dbh;
 
-my $error = '';
-if ($reasonnum == -1) {
-
-  $error = 'Enter a new reason (or select an existing one)'
-    unless $cgi->param('newreasonnum') !~ /^\s*$/;
-  my $reason = new FS::reason {
-                 'reason_type' => scalar($cgi->param('newreasonnumT')),
-                 'reason'      => scalar($cgi->param('newreasonnum')),
-               };
-  $error ||= $reason->insert;
-  $cgi->param('reasonnum', $reason->reasonnum)
-    unless $error;
+my ($reasonnum, $error) = $m->comp('/misc/process/elements/reason');
+if (!$reasonnum) {
+  $error ||= 'Reason required'
 }
 }
+$cgi->param('reasonnum', $reasonnum) unless $error;
 
 unless ($error) {
   my $new = new FS::cust_credit ( {
 
 unless ($error) {
   my $new = new FS::cust_credit ( {
index b7a715b..c5b3d6f 100755 (executable)
@@ -29,109 +29,119 @@ Example:
 
 </%doc>
 
 
 </%doc>
 
+% # note style improvements.
+% # - no more conditionally included code here
+% # - callers are not expected to pass javascript fragments
+% # - no redundant checking of ACLs or parameters
+% # - form fields are grouped for easy management
+% # - use the standard select-table widget instead of ad hoc crap
 <SCRIPT TYPE="text/javascript">
 <SCRIPT TYPE="text/javascript">
-  function sh_add<% $func_suffix %>()
-  {
-    var hints = <% encode_json(\@hints) %>;
+  function <% $id %>_changed() {
+    var hints = <% encode_json(\%all_hints) %>;
     var select_reason = document.getElementById('<% $id %>');
 
     var select_reason = document.getElementById('<% $id %>');
 
-% if ( $class eq 'S' ) {    
     document.getElementById('<% $id %>_hint').innerHTML =
     document.getElementById('<% $id %>_hint').innerHTML =
-      hints[select_reason.selectedIndex];
-% }
+      hints[select_reason.value] || '';
 
 
-    if (select_reason.selectedIndex == 0){
-      <% $controlledbutton ? $controlledbutton.'.disabled = true;' : ';' %>
-    }else{
-      <% $controlledbutton ? $controlledbutton.'.disabled = false;' : ';' %>
+    // toggle submit button state
+    var submit_button = document.getElementById(<% $opt{control_button} |js_string %>);
+    if (submit_button) {
+      submit_button.disabled = ( select_reason.value == 0 );
     }
 
     }
 
-%if ($curuser->access_right($add_access_right)){
-
-    if (select_reason.selectedIndex == 
-         (select_reason.length - 1)) {
-      document.getElementById('new<% $id %>').disabled = false;
-      document.getElementById('new<% $id %>').style.display = 'inline';
-      document.getElementById('new<% $id %>Label').style.display = 'inline';
-      document.getElementById('new<% $id %>T').disabled = false;
-      document.getElementById('new<% $id %>T').style.display = 'inline';
-      document.getElementById('new<% $id %>TLabel').style.display = 'inline';
+    // toggle visibility of 'new reason' fields
+    var new_fields = document.getElementById('<% $id %>_new_fields');
+    if ( select_reason.value == -1 ) {
+      new_fields.disabled = false;
+      new_fields.style.display = '';
     } else {
     } else {
-      document.getElementById('new<% $id %>').disabled = true;
-      document.getElementById('new<% $id %>').style.display = 'none';
-      document.getElementById('new<% $id %>Label').style.display = 'none';
-      document.getElementById('new<% $id %>T').disabled = true;
-      document.getElementById('new<% $id %>T').style.display = 'none';
-      document.getElementById('new<% $id %>TLabel').style.display = 'none';
+      new_fields.disabled = true;
+      new_fields.style.display = 'none';
     }
 
     }
 
-%}
-
   }
   }
+  <&| onload.js &> <% $id %>_changed(); </&>
 </SCRIPT>
 
 </SCRIPT>
 
-<TR>
-  <TD ALIGN="right"><% mt('Reason') |h %></TD>
-  <TD>
-    <SELECT id="<% $id %>" name="<% $name %>" onFocus="sh_add<% $func_suffix %>()" onChange="sh_add<% $func_suffix %>()">
-      <OPTION VALUE="" <% ($init_reason eq '') ? 'SELECTED' : '' %>><% mt('Select Reason...') |h %></OPTION>
-%    foreach my $reason (@reasons) {
-      <OPTION VALUE="<% $reason->reasonnum %>" <% ($init_reason == $reason->reasonnum) ? 'SELECTED' : '' %>><% $reason->reasontype->type %> : <% $reason->reason %></OPTION>
-%    }
-%    if ($curuser->access_right($add_access_right)) {
-      <OPTION VALUE="-1" <% ($init_reason == -1) ? 'SELECTED' : '' %>><% mt('Add new reason') |h %></OPTION>
-%    }
-%
-    </SELECT>
-  </TD>
-</TR>
-
-%   my @types = qsearch( 'reason_type', { 'class' => $class } );
-%   if (scalar(@types) < 1) {  # we should never reach this
-<TR>
-  <TD ALIGN="right">
-    <P><% mt('No reason types. Please add some.') |h %></P>
-  </TD>
-</TR>
-%   }elsif (scalar(@types) == 1) {
-<TR>
-  <TD ALIGN="right">
-    <P id="new<% $name %>TLabel" style="display:<% $display %>"><% mt('Reason Type') |h %></P>
-  </TD>
-  <TD>
-    <P id="new<% $name %>T" disabled="<% $disabled %>" style="display:<% $display %>"><% $types[0]->type %>
-    <INPUT type="hidden" name="new<% $name %>T" value="<% $types[0]->typenum %>">
-  </TD>
-</TR>
-
-%   }else{
-
-<TR>
-  <TD ALIGN="right">
-    <P id="new<% $id %>TLabel" style="display:<% $display %>"><% mt('Reason Type') |h %></P>
-  </TD>
-  <TD>
-    <SELECT id="new<% $id %>T" name="new<% $name %>T" "<% $disabled %>" style="display:<% $display %>">
-%     for my $type (@types) {
-        <OPTION VALUE="<% $type->typenum %>" <% ($init_type == $type->typenum) ? 'SELECTED' : '' %>><% $type->type %></OPTION>
-%     }
-    </SELECT>
-  </TD>
-</TR>
-%   }
+%# sadly can't just use add_inline here, as we have non-text fields
+<& tr-select-table.html,
+  'label'           => 'Reason',
+  'field'           => $name,
+  'id'              => $id,
+  'table'           => 'reason',
+  'records'         => \@reasons,
+  'label_callback'  => sub { my $reason = shift;
+                             $reason->type . ' : ' .  $reason->reason },
+  'disable_empty'   => 1,
+  'pre_options'     => [ 0 => 'Select reason...' ],
+  'post_options'    => [ -1 => 'Add new reason' ],
+  'curr_value'      => $init_reason,
+  'onchange'        => $id.'_changed()',
+&>
+
+% # "add new reason" fields
+% # should be a <fieldset>, but that doesn't fit well into the table
+
+<TR id="<% $id %>_new_fields">
+  <TD COLSPAN=2>
+    <TABLE CLASS="inv" STYLE="text-align: left">
+
+      <& tr-input-text.html,
+        label => 'New reason',
+        field => $id.'_new_reason'
+      &>
+
+% my @types = qsearch( 'reason_type', { 'class' => $class } );
+% if (scalar(@types) < 1) {  # we should never reach this
+      <TR>
+        <TD ALIGN="right">
+          <P><% mt('No reason types. Please add some.') |h %></P>
+        </TD>
+      </TR>
+% } elsif (scalar(@types) == 1) {
+      <& tr-fixed.html,
+        label => 'Reason type',
+        field => $id.'_new_reason_type',
+        curr_value => $types[0]->typenum,
+        formatted_value => $types[0]->type,
+      &>
+% } else { # more than one type, the normal case
+      <& tr-select-table.html,
+        label         => 'Reason type',
+        field         => $id.'_new_reason_type',
+        table         => 'reason_type',
+        name_col      => 'type',
+        hashref       => { 'class' => $class },
+        disable_empty => 1,
+      &>
+% } # scalar(@types)
 
 % if ( $class eq 'S' ) {
 
 % if ( $class eq 'S' ) {
-<TR>
-  <TD COLSPAN=2 ALIGN="center" id="<% $id %>_hint">
-  </TD>
-</TR>
+      <& tr-checkbox.html,
+        label => 'Credit the unused portion of service when suspending',
+        field => $id.'_new_unused_credit',
+        value => 'Y'
+      &>
+      <& tr-select-part_pkg.html,
+        label   => 'Charge this fee when unsuspending',
+        field   => $id.'_new_unsuspend_pkgpart',
+        hashref => { disabled => '', freq => '0' },
+        empty_label => 'none',
+      &>
+      <& tr-checkbox.html,
+        label => 'Hold unsuspension fee until the next bill',
+        field => $id.'_new_unsuspend_hold',
+        value => 'Y',
+      &>
 % }
 % }
+    </table>
+  </td>
+</tr>
 
 
+% # container for hints
 <TR>
 <TR>
-  <TD ALIGN="right">
-    <P id="new<% $id %>Label" style="display:<% $display %>"><% mt('New Reason') |h %></P>
+  <TD COLSPAN=2 ALIGN="center" id="<% $id %>_hint" style="font-size:small">
   </TD>
   </TD>
-  <TD><INPUT id="new<% $id %>" name="new<% $name %>" type="text" value="<% $init_newreason |h %>" "<% $disabled %>" style="display:<% $display %>"></TD>
 </TR>
 
 <%init>
 </TR>
 
 <%init>
@@ -148,11 +158,7 @@ if ( $opt{'cgi'} ) {
   $init_reason = $opt{'curr_value'};
 }
 
   $init_reason = $opt{'curr_value'};
 }
 
-my $controlledbutton = $opt{'control_button'};
-
-( my $func_suffix = $name ) =~ s/\./_/g;
-
-my $id = $opt{'id'} || $func_suffix;
+my $id = $opt{'id'} || $name;
 
 my $add_access_right;
 if ($class eq 'C') {
 
 my $add_access_right;
 if ($class eq 'C') {
@@ -167,41 +173,21 @@ if ($class eq 'C') {
   die "illegal class: $class";
 }
 
   die "illegal class: $class";
 }
 
-my( $display, $disabled ) = ( 'none', 'DISABLED' );
-my( $init_type, $init_newreason ) = ( '', '' );
-if ($init_reason == -1 || ref($init_reason) ) {
-
-  $display = 'inline';
-  $disabled = '';
-
-  if ( ref($init_reason) ) {
-    $init_type      = $init_reason->{'typenum'};
-    $init_newreason = $init_reason->{'reason'};
-    $init_reason = -1;
-  } elsif ( $opt{'cgi'} ) {
-    $init_type      = $opt{'cgi'}->param( "new${name}T" );
-    $init_newreason = $opt{'cgi'}->param( "new$name"    );
-  }
-
-}
-
-my $extra_sql =
-  "WHERE class = '$class' and (disabled = '' OR disabled is NULL)";
-
 my @reasons = qsearch({
 my @reasons = qsearch({
-  table     => 'reason', 
-  hashref   => {},
-  extra_sql => $extra_sql,
-  addl_from => 'LEFT JOIN reason_type '.
-               ' ON reason_type.typenum = reason.reason_type',
-  order_by  => 'ORDER BY reason_type.type ASC, reason.reason ASC',
+  'table'           => 'reason',
+  'addl_from'       => ' LEFT JOIN reason_type'.
+                       ' ON (reason.reason_type = reason_type.typenum)',
+  'hashref'         => { disabled => '' },
+  'extra_sql'       => " AND reason_type.class = '$class'",
+  'order_by'        => ' ORDER BY type, reason',
 });
 
 });
 
-my @hints;
+my %all_hints;
 if ( $class eq 'S' ) {
   my $conf = FS::Conf->new;
 if ( $class eq 'S' ) {
   my $conf = FS::Conf->new;
-  @hints = ( '' );
+  %all_hints = ( 0 => '', -1 => '' );
   foreach my $reason (@reasons) {
   foreach my $reason (@reasons) {
+    my @hints;
     if ( $reason->unsuspend_pkgpart ) {
       my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart);
       if ( $part_pkg ) {
     if ( $reason->unsuspend_pkgpart ) {
       my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart);
       if ( $part_pkg ) {
@@ -225,15 +211,13 @@ if ( $class eq 'S' ) {
           '<FONT COLOR="#ff0000">Unsuspend pkg #'.$reason->unsuspend_pkgpart.
           ' not found.</FONT>';
       }
           '<FONT COLOR="#ff0000">Unsuspend pkg #'.$reason->unsuspend_pkgpart.
           ' not found.</FONT>';
       }
-    } else { #no unsuspend_pkgpart
-      push @hints, '';
     }
     }
+    if ( $reason->unused_credit ) {
+      push @hints, mt('The customer will be credited for unused time.');
+    }
+    $all_hints{ $reason->reasonnum } = join('<BR>', @hints);
   }
   }
-  push @hints, ''; # for the "new reason" case
-  @hints = map {'<FONT SIZE="-1">'.$_.'</FONT>'} @hints;
 }
 
 }
 
-
 my $curuser = $FS::CurrentUser::CurrentUser;
 my $curuser = $FS::CurrentUser::CurrentUser;
-
 </%init>
 </%init>
index 3259a03..e4bfdba 100644 (file)
@@ -50,7 +50,7 @@ STYLE="margin-left:auto; margin-right:auto">
              'field'          => 'reasonnum',
              'reason_class'   => 'C',
              'cgi'            => $cgi,
              'field'          => 'reasonnum',
              'reason_class'   => 'C',
              'cgi'            => $cgi,
-             'control_button' => "document.getElementById('confirm_cancel_cust_button')",
+             'control_button' => 'confirm_cancel_cust_button',
 &>
 
 </TABLE>
 &>
 
 </TABLE>
index e2734e9..c80b2b2 100755 (executable)
@@ -6,9 +6,9 @@
 <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
 <INPUT TYPE="hidden" NAME="method" VALUE="<% $method %>">
 
 <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
 <INPUT TYPE="hidden" NAME="method" VALUE="<% $method %>">
 
-<BR><BR>
+<BR>
 <% emt(ucfirst($method)." [_1]", $part_pkg->pkg_comment(cust_pkg=>$cust_pkg) ) %>
 <% emt(ucfirst($method)." [_1]", $part_pkg->pkg_comment(cust_pkg=>$cust_pkg) ) %>
-<% ntable("#cccccc", 2) %>
+<table style="background-color: #cccccc; border-spacing: 2; width: 100%">
 
 % my $date_init = 0;
 % if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
 
 % my $date_init = 0;
 % if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
@@ -58,7 +58,7 @@
        field          => 'reasonnum',
        reason_class   => $class,
        curr_value     => $reasonnum,
        field          => 'reasonnum',
        reason_class   => $class,
        curr_value     => $reasonnum,
-       control_button => "document.getElementById('confirm_cancel_pkg_button')",
+       control_button => "confirm_cancel_pkg_button",
   &>
 % }
 
   &>
 % }
 
index 2ae9f10..a78a8b3 100755 (executable)
@@ -22,51 +22,43 @@ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
   $custnum = $1;
 }
 
   $custnum = $1;
 }
 
-#false laziness w/process/cancel_pkg.html
 
 
-#untaint reasonnum
-my $reasonnum = $cgi->param('reasonnum');
-$reasonnum =~ /^(-?\d+)$/ || die "Illegal reasonnum";
-$reasonnum = $1;
-
-if ($reasonnum == -1) {
-  $reasonnum = {
-    'typenum' => scalar( $cgi->param('newreasonnumT') ),
-    'reason'  => scalar( $cgi->param('newreasonnum' ) ),
-  };
+#untaint reasonnum / create new reason
+my ($reasonnum, $error) = $m->comp('process/elements/reason');
+if (!$reasonnum) {
+  $error ||= 'Reason required'
 }
 
 }
 
-#eslaf
-
 my $cust_main = qsearchs( {
   'table'     => 'cust_main',
   'hashref'   => { 'custnum' => $custnum },
   'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
 } );
 
 my $cust_main = qsearchs( {
   'table'     => 'cust_main',
   'hashref'   => { 'custnum' => $custnum },
   'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
 } );
 
-my @errors;
-if($cgi->param('now_or_later')) {
+if ( $error ) {
+  # do nothing
+} elsif ( $cgi->param('now_or_later') ) {
   $expire = parse_datetime($expire);
   if($expire) {
     #warn "setting expire dates on custnum#$custnum\n";
     my @pkgs = $cust_main->ncancelled_pkgs;
   $expire = parse_datetime($expire);
   if($expire) {
     #warn "setting expire dates on custnum#$custnum\n";
     my @pkgs = $cust_main->ncancelled_pkgs;
-    @errors = grep {$_} map { $_->cancel(
+    my @errors = grep {$_} map { $_->cancel(
       'reason'  => $reasonnum,
       'date'    => $expire,
     ) } @pkgs;
       'reason'  => $reasonnum,
       'date'    => $expire,
     ) } @pkgs;
+    $error = join(' / ', @errors);
   }
   else {
   }
   else {
-    @errors = ("error parsing expire date: ".$cgi->param('expire'));
+    $error = ("error parsing expire date: ".$cgi->param('expire'));
   }
 }
 else {
   warn "cancelling $cust_main";
   }
 }
 else {
   warn "cancelling $cust_main";
-  @errors = $cust_main->cancel(
+  $error = $cust_main->cancel(
     'ban'    => $ban,
     'reason' => $reasonnum,
   );
 }
     'ban'    => $ban,
     'reason' => $reasonnum,
   );
 }
-my $error = join(' / ', @errors) if scalar(@errors);
 
 if ( $error ) {
   $cgi->param('error', $error);
 
 if ( $error ) {
   $cgi->param('error', $error);
index 6185136..7a501d6 100755 (executable)
@@ -22,50 +22,39 @@ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
   $custnum = $1;
 }
 
   $custnum = $1;
 }
 
-#false laziness w/process/cancel_pkg.html
-
-#untaint reasonnum
-my $reasonnum = $cgi->param('reasonnum');
-$reasonnum =~ /^(-?\d+)$/ || die "Illegal reasonnum";
-$reasonnum = $1;
-
-if ($reasonnum == -1) {
-  $reasonnum = {
-    'typenum' => scalar( $cgi->param('newreasonnumT') ),
-    'reason'  => scalar( $cgi->param('newreasonnum' ) ),
-  };
+#untaint reasonnum / create new reason
+my ($reasonnum, $error) = $m->comp('process/elements/reason');
+if (!$reasonnum) {
+  $error ||= 'Reason required';
 }
 
 }
 
-#eslaf
-
 my $cust_main = qsearchs( {
   'table'     => 'cust_main',
   'hashref'   => { 'custnum' => $custnum },
   'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
 } );
 
 my $cust_main = qsearchs( {
   'table'     => 'cust_main',
   'hashref'   => { 'custnum' => $custnum },
   'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
 } );
 
-my @errors;
-if($cgi->param('now_or_later')) {
+if ( $error ) {
+  # do nothing
+} elsif ( $cgi->param('now_or_later') ) {
   $adjourn = parse_datetime($adjourn);
   if($adjourn) {
     #warn "setting adjourn dates on custnum#$custnum\n";
     my @pkgs = $cust_main->unsuspended_pkgs;
   $adjourn = parse_datetime($adjourn);
   if($adjourn) {
     #warn "setting adjourn dates on custnum#$custnum\n";
     my @pkgs = $cust_main->unsuspended_pkgs;
-    @errors = grep {$_} map { $_->suspend(
+    my @errors = grep {$_} map { $_->suspend(
       'reason'  => $reasonnum,
       'date'    => $adjourn,
     ) } @pkgs;
       'reason'  => $reasonnum,
       'date'    => $adjourn,
     ) } @pkgs;
+    $error = join(' / ', @errors);
+  } else {
+    $error = ("error parsing adjourn date: ".$cgi->param('adjourn'));
   }
   }
-  else {
-    @errors = ("error parsing adjourn date: ".$cgi->param('adjourn'));
-  }
-}
-else {
+} else {
   warn "suspending $cust_main";
   warn "suspending $cust_main";
-  @errors = $cust_main->suspend(
+  $error = $cust_main->suspend(
     'reason' => $reasonnum,
   );
 }
     'reason' => $reasonnum,
   );
 }
-my $error = join(' / ', @errors) if scalar(@errors);
 
 if ( $error ) {
   $cgi->param('error', $error);
 
 if ( $error ) {
   $cgi->param('error', $error);
index a106b84..47ceca2 100755 (executable)
@@ -65,17 +65,12 @@ if ( $method eq 'suspend' ) { #or 'adjourn'
 
 my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
 
 
 my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
 
-#untaint reasonnum
-my $reasonnum = $cgi->param('reasonnum');
-if ( $method !~ /^(unsuspend|uncancel)$/ ) {
-  $reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
-  $reasonnum = $1;
-
-  if ($reasonnum == -1) {
-    $reasonnum = {
-      'typenum' => scalar( $cgi->param('newreasonnumT') ),
-      'reason'  => scalar( $cgi->param('newreasonnum' ) ),
-    };
+#untaint reasonnum, and set up new reason if appropriate
+my ($reasonnum, $error);
+if ($method ne 'resume' and $method ne 'uncancel') {
+  ($reasonnum, $error) = $m->comp('elements/reason');
+  if (!$reasonnum) {
+    $error ||= 'Reason required';
   }
 }
 
   }
 }
 
@@ -87,7 +82,7 @@ my $bill =
 
 my $svc_fatal = ( $cgi->param('svc_not_fatal') ne 'Y' );
 
 
 my $svc_fatal = ( $cgi->param('svc_not_fatal') ne 'Y' );
 
-my $error = $cust_pkg->$method( 'reason'      => $reasonnum,
+$error ||=  $cust_pkg->$method( 'reason'      => $reasonnum,
                                 'date'        => $date,
                                 'resume_date' => $resume_date,
                                 'last_bill'   => $last_bill,
                                 'date'        => $date,
                                 'resume_date' => $resume_date,
                                 'last_bill'   => $last_bill,
diff --git a/httemplate/misc/process/elements/reason b/httemplate/misc/process/elements/reason
new file mode 100644 (file)
index 0000000..ae92a75
--- /dev/null
@@ -0,0 +1,17 @@
+<%init>
+#untaint reasonnum, and set up new reason if appropriate
+my $reasonnum = $cgi->param('reasonnum');
+$reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+$reasonnum = $1;
+
+my $error;
+if ($reasonnum == -1) {
+  my $new_reason = FS::reason->new({
+    map { $_ => scalar( $cgi->param("reasonnum_new_$_") ) }
+    qw( reason_type reason unsuspend_pkgpart unsuspend_hold unused_credit )
+  }); # not sanitizing them here, but check() will do it
+  $error = $new_reason->insert;
+  $reasonnum = $new_reason->reasonnum;
+}
+return ($reasonnum, $error);
+</%init>
index e0d17f3..3a49e13 100644 (file)
@@ -42,7 +42,7 @@ STYLE="margin-left:auto; margin-right:auto">
              'field'          => 'reasonnum',
              'reason_class'   => 'S',
              'cgi'            => $cgi,
              'field'          => 'reasonnum',
              'reason_class'   => 'S',
              'cgi'            => $cgi,
-             'control_button' => "document.getElementById('confirm_suspend_cust_button')",
+             'control_button' => 'confirm_suspend_cust_button',
 &>
 
 </TABLE>
 &>
 
 </TABLE>
index 4880ac3..833b6d0 100755 (executable)
@@ -60,8 +60,8 @@ function areyousure(href, message) {
                 'actionlabel' => emt('Confirm Suspension'),
                 'color'       => '#ff9900',
                 'cust_main'   => $cust_main,
                 'actionlabel' => emt('Confirm Suspension'),
                 'color'       => '#ff9900',
                 'cust_main'   => $cust_main,
-                'width'       => 616, #make room for reasons
-                'height'      => 366,
+                'width'       => 768, #make room for reasons
+                'height'      => 450, 
               }
   &> | 
 % }
               }
   &> | 
 % }
@@ -91,7 +91,7 @@ function areyousure(href, message) {
                 'color'       => '#ff0000',
                 'cust_main'   => $cust_main,
                 'width'       => 616, #make room for reasons
                 'color'       => '#ff0000',
                 'cust_main'   => $cust_main,
                 'width'       => 616, #make room for reasons
-                'height'      => 366,
+                'height'      => 410,
               }
   &> | 
 % }
               }
   &> | 
 % }
index accdb45..f760d6f 100644 (file)
@@ -582,6 +582,7 @@ sub pkg_suspend_link {
              'actionlabel' => emt('Suspend'),
              'color'       => '#FF9900',
              'cust_pkg'    => shift,
              'actionlabel' => emt('Suspend'),
              'color'       => '#FF9900',
              'cust_pkg'    => shift,
+             'height'      => 420,
          )
 }
 
          )
 }
 
@@ -592,6 +593,7 @@ sub pkg_adjourn_link {
              'actionlabel' => emt('Adjourn'),
              'color'       => '#CC6600',
              'cust_pkg'    => shift,
              'actionlabel' => emt('Adjourn'),
              'color'       => '#CC6600',
              'cust_pkg'    => shift,
+             'height'      => 445,
          )
 }
 
          )
 }