[freeside-commits] branch master updated. 00938b30a69411a743aa01db5e27100818a3c82b

Mark Wells mark at 420.am
Tue Aug 14 17:03:02 PDT 2012


The branch, master has been updated
       via  00938b30a69411a743aa01db5e27100818a3c82b (commit)
      from  08ac47123e098939db3d94cc136ff02f174e8dd0 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 00938b30a69411a743aa01db5e27100818a3c82b
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Aug 14 17:02:47 2012 -0700

    unsuspend fees, #6587

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 6b32d71..0f1d151 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3671,6 +3671,8 @@ sub tables_hashref {
         'reason_type',   'int',  '', '', '', '', 
         'reason',        'text', '', '', '', '', 
         'disabled',      'char',    'NULL', 1, '', '', 
+        'unsuspend_pkgpart', 'int',  'NULL', '', '', '',
+        'unsuspend_hold','char',    'NULL', 1, '', '',
       ],
       'primary_key' => 'reasonnum',
       'unique' => [],
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index aed99e5..f56e1f0 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -1319,7 +1319,8 @@ sub credit_remaining {
 
 Unsuspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
 package, then unsuspends the package itself (clears the susp field and the
-adjourn field if it is in the past).
+adjourn field if it is in the past).  If the suspend reason includes an 
+unsuspension package, that package will be ordered.
 
 Available options are:
 
@@ -1423,6 +1424,8 @@ sub unsuspend {
 
   }
 
+  my $reason = $self->last_cust_pkg_reason('susp')->reason;
+
   my %hash = $self->hash;
   my $inactive = time - $hash{'susp'};
 
@@ -1449,6 +1452,33 @@ sub unsuspend {
     return $error;
   }
 
+  my $unsusp_pkg;
+
+  if ( $reason->unsuspend_pkgpart ) {
+    my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart)
+      or $error = "Unsuspend package definition ".$reason->unsuspend_pkgpart.
+                  " not found.";
+    my $start_date = $self->cust_main->next_bill_date 
+      if $reason->unsuspend_hold;
+
+    if ( $part_pkg ) {
+      $unsusp_pkg = FS::cust_pkg->new({
+          'custnum'     => $self->custnum,
+          'pkgpart'     => $reason->unsuspend_pkgpart,
+          'start_date'  => $start_date,
+          'locationnum' => $self->locationnum,
+          # discount? probably not...
+      });
+      
+      $error ||= $self->cust_main->order_pkg( 'cust_pkg' => $unsusp_pkg );
+    }
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
   if ( $conf->config('unsuspend_email_admin') ) {
  
     my $error = send_email(
@@ -1462,6 +1492,11 @@ sub unsuspend {
         'Customer: #'. $self->custnum. ' '. $self->cust_main->name. "\n",
         'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n",
         ( map { "Service : $_\n" } @labels ),
+        ($unsusp_pkg ?
+          "An unsuspension fee was charged: Package #".$unsusp_pkg->pkgnum.
+            " (.".$unsusp_pkg->pkg_comment.")\n"
+          : ''
+        ),
       ],
     );
 
diff --git a/FS/FS/reason.pm b/FS/FS/reason.pm
index 377da49..a9a7d74 100644
--- a/FS/FS/reason.pm
+++ b/FS/FS/reason.pm
@@ -46,6 +46,15 @@ FS::Record.  The following fields are currently supported:
 
 =item disabled - 'Y' or ''
 
+=item unsuspend_pkgpart - for suspension reasons only, the pkgpart (see
+L<FS::part_pkg>) of a package to be ordered when the package is unsuspended.
+Typically this will be some kind of reactivation fee.  Attaching it to 
+a suspension reason allows the reactivation fee to be charged for some
+suspensions but not others.
+
+=item unsuspend_hold - 'Y' or ''.  If unsuspend_pkgpart is set, this tells
+whether to bill the unsuspend package immediately ('') or to wait until 
+the customer's next invoice ('Y').
 
 =back
 
@@ -97,16 +106,30 @@ sub check {
 
   my $error = 
     $self->ut_numbern('reasonnum')
+    || $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;
 
+  if ( $self->reasontype->class eq 'S' ) {
+    $error = $self->ut_numbern('unsuspend_pkgpart')
+          || $self->ut_foreign_keyn('unsuspend_pkgpart', 'part_pkg', 'pkgpart')
+          || $self->ut_flag('unsuspend_hold')
+    ;
+    return $error if $error;
+  } else {
+    $self->set('unsuspend_pkgpart' => '');
+    $self->set('unsuspend_hold'    => '');
+  }
+
   $self->SUPER::check;
 }
 
 =item reasontype
 
-Returns the reason_type (see <I>FS::reason_type</I>) associated with this reason.
+Returns the reason_type (see L<FS::reason_type>) associated with this reason.
 
 =cut
 
@@ -118,7 +141,7 @@ sub reasontype {
 
 =head1 BUGS
 
-Here be termintes.  Don't use on wooden computers.
+Here by termintes.  Don't use on wooden computers.
 
 =head1 SEE ALSO
 
diff --git a/httemplate/browse/reason.html b/httemplate/browse/reason.html
index fe285be..14e97bf 100644
--- a/httemplate/browse/reason.html
+++ b/httemplate/browse/reason.html
@@ -17,14 +17,17 @@
                  'header'      => [ '#',
                                     ucfirst($classname) . ' Reason Type',
                                     ucfirst($classname) . ' Reason',
+                                    ($class eq 'S' ?  'Unsuspension Fee' : ()),
                                   ],
                  'fields'      => [ 'reasonnum',
                                     sub { shift->reasontype->type },
                                     'reason',
+                                    $unsuspend_pkg_comment,
                                   ],
                  'links'       => [ $link,
                                     $link,
                                     '',
+                                    $unsuspend_pkg_link,
                                   ],
              )
 %>
@@ -50,4 +53,18 @@ my $count_query = 'SELECT COUNT(*) FROM reason LEFT JOIN reason_type on ' .
 
 my $link = [ $p."edit/reason.html?class=$class&reasonnum=", 'reasonnum' ];
 
+my ($unsuspend_pkg_comment, $unsuspend_pkg_link);
+if ( $class eq 'S' ) {
+  $unsuspend_pkg_comment = sub {
+    my $pkgpart = shift->unsuspend_pkgpart or return '';
+    my $part_pkg = FS::part_pkg->by_key($pkgpart) or return '';
+    $part_pkg->pkg_comment;
+  };
+
+  my $unsuspend_pkg_link = sub {
+    my $pkgpart = shift->unsuspend_pkgpart or return '';
+    [ $p."edit/part_pkg.cgi?", $pkgpart ];
+  };
+}
+
 </%init>
diff --git a/httemplate/edit/reason.html b/httemplate/edit/reason.html
index 620a2ea..78d0447 100644
--- a/httemplate/edit/reason.html
+++ b/httemplate/edit/reason.html
@@ -1,50 +1,78 @@
-%
-% $cgi->param('class') =~ /^(\w)$/ or die "illegal class";
-% my $class=$1;
-%
-% my $classname = $FS::reason_type::class_name{$class};
-%
-% my (@types) = qsearch( 'reason_type', { 'class' => $class } );
-%
-% unless (scalar(@types)) {
-%   print $cgi->redirect( "reason_type.html?class=$class" );
-% }
-<% include( 'elements/edit.html',
-                 'name'   => ucfirst($classname) . ' Reason',
-                 'table'  => 'reason',
-                 'labels' => { 
-                               'reasonnum'   => ucfirst($classname) .  ' Reason',
-                               'reason_type' => ucfirst($classname) . ' Reason type',
-                               'reason'      => ucfirst($classname) . ' Reason',
-			       'disabled'    => 'Disabled',
-                               'class'       => '',
-                             },
-		 'fields' => [
-			       { 'field' => 'reason_type',
-			         'type'  => 'select',
-                                 #XXX use something more sane than a hashref
-                                 #then fix tr-select.html
-				 'value' => { 'vcolumn' => 'typenum',
-				              'ccolumn' => 'type',
-					      'values'  => \@types,
-					    },
-			       },
-			       'reason',
-			       { 'field' => 'class',
-			         'type'  => 'hidden',
-				 'value' => $class,
-			       },
-			       { 'field' => 'disabled',
-			         'type'  => 'checkbox',
-				 'value' => 'Y'
-			       },
-		             ],
-                 'viewall_url' => $p . "browse/reason.html?class=$class",
-           )
-%>
+<& elements/edit.html,
+  'menubar'=> [ "View all $classname Reasons" => 
+                  $p.'browse/reason.html?class='.$class,
+                "View $classname Reason Types" =>
+                  $p.'browse/reason_type.html?class='.$class,
+              ],
+  'name'   => ucfirst($classname) . ' Reason',
+  
+  'table'  => 'reason',
+  'labels' => { 
+                'reasonnum'   => $classname .  ' Reason',
+                'reason_type' => $classname . ' Reason type',
+                'reason'      => $classname . ' Reason',
+ 	        'disabled'    => 'Disabled',
+                'class'       => '',
+                'unsuspend_pkgpart' => 'Unsuspension fee',
+                'unsuspend_hold'    => 'Delay until next bill',
+              },
+  'fields' => \@fields,
+&>
 <%init>
 
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
+$cgi->param('class') =~ /^(\w)$/ or die "illegal class";
+my $class=$1;
+
+my $classname = ucfirst($FS::reason_type::class_name{$class});
+
+my (@types) = qsearch( 'reason_type', { 'class' => $class } );
+
+unless (scalar(@types)) {
+  print $cgi->redirect( "reason_type.html?class=$class" );
+}
+
+my @fields = (
+  { 'field' => 'reason_type',
+    'type'  => 'select-table',
+    'table' => 'reason_type',
+    'name_col'  => 'type',
+    'value_col' => 'typenum',
+    'hashref'   => { 'class' => $class },
+    'disable_empty' => 1,
+#     #then fix tr-select.html
+#
+#    'value' => { 'vcolumn' => 'typenum',
+#                 'ccolumn' => 'type',
+#   	      'values'  => \@types,
+#   	    },
+#     # that wasn't so hard...did this do something else that I'm missing?
+  },
+  'reason',
+  { 'field' => 'class',
+    'type'  => 'hidden',
+    'value' => $class,
+  },
+  { 'field' => 'disabled',
+    'type'  => 'checkbox',
+    'value' => 'Y'
+  },
+);
+
+push @fields,
+  { 'field'     => 'unsuspend_pkgpart',
+    'type'      => 'select-part_pkg',
+    'hashref'   => { 'disabled' => '',
+                     'freq'     => 0 }, # one-time charges only
+  },
+  { 'field'     => 'unsuspend_hold',
+    'type'      => 'checkbox',
+    'value'     => 'Y',
+  },
+  if ( $class eq 'S' );
+  
+
+
 </%init>
diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
index 5a79d68..c1df10b 100755
--- a/httemplate/elements/tr-select-reason.html
+++ b/httemplate/elements/tr-select-reason.html
@@ -32,8 +32,15 @@ Example:
 <SCRIPT TYPE="text/javascript">
   function sh_add<% $func_suffix %>()
   {
+    var hints = <% encode_json(\@hints) %>;
+    var select_reason = document.getElementById('<% $id %>');
 
-    if (document.getElementById('<% $id %>').selectedIndex == 0){
+% if ( $class eq 'S' ) {    
+    document.getElementById('<% $id %>_hint').innerHTML =
+      hints[select_reason.selectedIndex];
+% }
+
+    if (select_reason.selectedIndex == 0){
       <% $controlledbutton ? $controlledbutton.'.disabled = true;' : ';' %>
     }else{
       <% $controlledbutton ? $controlledbutton.'.disabled = false;' : ';' %>
@@ -41,8 +48,8 @@ Example:
 
 %if ($curuser->access_right($add_access_right)){
 
-    if (document.getElementById('<% $id %>').selectedIndex == 
-         (document.getElementById('<% $id %>').length - 1)) {
+    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';
@@ -113,6 +120,13 @@ Example:
 </TR>
 %   }
 
+% if ( $class eq 'S' ) {
+<TR>
+  <TD COLSPAN=2 ALIGN="center" id="<% $id %>_hint">
+  </TD>
+</TR>
+% }
+
 <TR>
   <TD ALIGN="right">
     <P id="new<% $id %>Label" style="display:<% $display %>"><% mt('New Reason') |h %></P>
@@ -184,6 +198,43 @@ my @reasons = qsearch({
   order_by  => 'ORDER BY reason_type.type ASC, reason.reason ASC',
 });
 
+my @hints;
+if ( $class eq 'S' ) {
+  my $conf = FS::Conf->new;
+  @hints = ( '' );
+  foreach my $reason (@reasons) {
+    if ( $reason->unsuspend_pkgpart ) {
+      my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart);
+      if ( $part_pkg ) {
+        if ( $part_pkg->option('setup_fee',1) > 0 and 
+             $part_pkg->option('recur_fee',1) == 0 ) {
+          # the usual case
+          push @hints,
+            mt('A [_1] unsuspension fee will apply.', 
+               ($conf->config('money_char') || '$') .
+               sprintf('%.2f', $part_pkg->option('setup_fee'))
+               );
+        } else {
+          # oddball cases--not really supported
+          push @hints,
+            mt('An unsuspension package will apply: [_1]',
+              $part_pkg->price_info
+              );
+        }
+      } else { #no $part_pkg
+        push @hints,
+          '<FONT COLOR="#ff0000">Unsuspend pkg #'.$reason->unsuspend_pkgpart.
+          ' not found.</FONT>';
+      }
+    } else { #no unsuspend_pkgpart
+      push @hints, '';
+    }
+  }
+  push @hints, ''; # for the "new reason" case
+  @hints = map {'<FONT SIZE="-1">'.$_.'</FONT>'} @hints;
+}
+
+
 my $curuser = $FS::CurrentUser::CurrentUser;
 
 </%init>

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

Summary of changes:
 FS/FS/Schema.pm                           |    2 +
 FS/FS/cust_pkg.pm                         |   37 +++++++++-
 FS/FS/reason.pm                           |   27 ++++++-
 httemplate/browse/reason.html             |   17 ++++
 httemplate/edit/reason.html               |  116 ++++++++++++++++++-----------
 httemplate/elements/tr-select-reason.html |   57 +++++++++++++-
 6 files changed, 206 insertions(+), 50 deletions(-)




More information about the freeside-commits mailing list