[freeside-commits] branch master updated. 6aa1a0eeb1c28caf6af94a1323f69f3bb4256302

Ivan ivan at 420.am
Wed May 2 20:47:22 PDT 2012


The branch, master has been updated
       via  6aa1a0eeb1c28caf6af94a1323f69f3bb4256302 (commit)
      from  12555eec358d00a201f5d5321cd15c925a0cc503 (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 6aa1a0eeb1c28caf6af94a1323f69f3bb4256302
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Wed May 2 20:47:21 2012 -0700

    un-cancel, RT#17518

diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index a11ad7f..914724c 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -138,6 +138,7 @@ tie my %rights, 'Tie::IxHash',
     'Unsuspend customer package',
     'Cancel customer package immediately',
     'Cancel customer package later',
+    'Un-cancel customer package',
     'Delay suspension events',
     'Add on-the-fly cancel reason', #NEW
     'Add on-the-fly suspend reason', #NEW
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index fb605ad..00c519e 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1512,6 +1512,8 @@ sub tables_hashref {
         'adjourn',        @date_type,             '', '', 
         'resume',         @date_type,             '', '', 
         'cancel',         @date_type,             '', '', 
+        'uncancel',       @date_type,             '', '', 
+        'uncancel_pkgnum',     'int', 'NULL', '', '', '',
         'expire',         @date_type,             '', '', 
         'contract_end',   @date_type,             '', '',
         'dundate',        @date_type,             '', '',
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index fc01746..26a480b 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -205,6 +205,7 @@ sub _upgrade_data { # class method
                             'Usage: Call Detail Records (CDRs)',
                             'Usage: Unrateable CDRs',
                           ],
+    'Cancel customer package immediately' => 'Un-cancel customer package',
   );
 
   foreach my $old_acl ( keys %onetime ) {
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 4359de9..5ccdb35 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -12,7 +12,7 @@ use Time::Local qw( timelocal timelocal_nocheck );
 use MIME::Entity;
 use FS::UID qw( getotaker dbh );
 use FS::Misc qw( send_email );
-use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( qsearch qsearchs fields );
 use FS::CurrentUser;
 use FS::cust_svc;
 use FS::part_pkg;
@@ -879,6 +879,143 @@ sub cancel_if_expired {
   '';
 }
 
+=item uncancel
+
+"Un-cancels" this package: Orders a new package with the same custnum, pkgpart,
+locationnum, (other fields?).  Attempts to re-provision cancelled services
+using history information (errors at this stage are not fatal).
+
+cust_pkg: pass a scalar reference, will be filled in with
+
+svc_errors: pass an array reference, will be filled in with any provisioning errors
+
+=cut
+
+sub uncancel {
+  my( $self, %options ) = @_;
+
+  #in case you try do do $uncancel-date = $cust_pkg->uncacel 
+  return '' unless $self->get('cancel');
+
+  ##
+  # Transaction-alize
+  ##
+
+  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;
+
+  ##
+  # insert the new package
+  ##
+
+  my $cust_pkg = new FS::cust_pkg {
+    last_bill       => ( $options{'last_bill'} || $self->get('last_bill') ),
+    bill            => ( $options{'bill'}      || $self->get('bill')      ),
+    uncancel        => time,
+    uncancel_pkgnum => $self->pkgnum,
+    map { $_ => $self->get($_) } qw(
+      custnum pkgpart locationnum
+      setup
+      susp adjourn resume expire start_date contract_end dundate
+      change_date change_pkgpart change_locationnum
+      manual_flag no_auto quantity agent_pkgid recur_show_zero setup_show_zero
+    ),
+  };
+
+  my $error = $cust_pkg->insert(
+    'change' => 1, #supresses any referral credit to a referring customer
+  );
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  ##
+  # insert services
+  ##
+
+  #find historical services within this timeframe before the package cancel
+  # (incompatible with "time" option to cust_pkg->cancel?)
+  my $fuzz = 2 * 60; #2 minutes?  too much?   (might catch separate unprovision)
+                     #            too little? (unprovisioing export delay?)
+  my($end, $start) = ( $self->get('cancel'), $self->get('cancel') - $fuzz );
+  my @h_cust_svc = $self->h_cust_svc( $end, $start );
+
+  my @svc_errors;
+  foreach my $h_cust_svc (@h_cust_svc) {
+    my $h_svc_x = $h_cust_svc->h_svc_x( $end, $start );
+    #next unless $h_svc_x; #should this happen?
+    (my $table = $h_svc_x->table) =~ s/^h_//;
+    require "FS/$table.pm";
+    my $class = "FS::$table";
+    my $svc_x = $class->new( {
+      'pkgnum'  => $cust_pkg->pkgnum,
+      'svcpart' => $h_cust_svc->svcpart,
+      map { $_ => $h_svc_x->get($_) } fields($table)
+    } );
+
+    # radius_usergroup
+    if ( $h_svc_x->isa('FS::h_svc_Radius_Mixin') ) {
+      $svc_x->usergroup( [ $h_svc_x->h_usergroup($end, $start) ] );
+    }
+
+    my $svc_error = $svc_x->insert;
+    if ( $svc_error ) { #&& $options{svc_fatal} ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+    push @svc_errors, $svc_error if $svc_error;
+  }
+
+  #these are pretty rare, but should handle them
+  # - dsl_device (mac addresses)
+  # - phone_device (mac addresses)
+  # - dsl_note (ikano notes)
+  # - domain_record (i.e. restore DNS information w/domains)
+  # - inventory_item(?) (inventory w/un-cancelling service?)
+  # - nas (svc_broaband nas stuff)
+  #this stuff is unused in the wild afaik
+  # - mailinglistmember
+  # - router.svcnum?
+  # - svc_domain.parent_svcnum?
+  # - acct_snarf (ancient mail fetching config)
+  # - cgp_rule (communigate)
+  # - cust_svc_option (used by our Tron stuff)
+  # - acct_rt_transaction (used by our time worked stuff)
+
+  ##
+  # also move over any services that didn't unprovision at cancellation
+  ## 
+
+  foreach my $cust_svc ( qsearch('cust_svc', { pkgnum => $self->pkgnum } ) ) {
+    $cust_svc->pkgnum( $cust_pkg->pkgnum );
+    my $error = $cust_svc->replace;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  ##
+  # Finish
+  ##
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  ${ $options{cust_pkg} }   = $cust_pkg   if ref($options{cust_pkg});
+  @{ $options{svc_errors} } = @svc_errors if ref($options{svc_errors});
+
+  '';
+}
+
 =item unexpire
 
 Cancels any pending expiration (sets the expire field to null).
diff --git a/FS/FS/h_radius_usergroup.pm b/FS/FS/h_radius_usergroup.pm
new file mode 100644
index 0000000..bbccd6b
--- /dev/null
+++ b/FS/FS/h_radius_usergroup.pm
@@ -0,0 +1,24 @@
+package FS::h_radius_usergroup;
+
+use strict;
+use base qw( FS::h_Common FS::radius_usergroup );
+
+sub table { 'h_radius_usergroup' };
+
+=head1 NAME
+
+FS::h_radius_usergroup - Historical RADIUS usergroup records.
+
+=head1 DESCRIPTION
+
+An FS::h_radius_usergroup object represents historical changes to an account's
+RADIUS group (L<FS::radius_usergroup>).
+
+=head1 SEE ALSO
+
+L<FS::radius_usergroup>,  L<FS::h_Common>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_Radius_Mixin.pm b/FS/FS/h_svc_Radius_Mixin.pm
new file mode 100644
index 0000000..af29770
--- /dev/null
+++ b/FS/FS/h_svc_Radius_Mixin.pm
@@ -0,0 +1,17 @@
+package FS::h_svc_Radius_Mixin;
+
+use strict;
+use FS::Record qw( qsearch );
+use FS::h_radius_usergroup;
+
+sub h_usergroup {
+  my $self = shift;
+  map { $_->groupnum } 
+    qsearch( 'h_radius_usergroup',
+             { svcnum => $self->svcnum },
+             FS::h_radius_usergroup->sql_h_searchs(@_),
+           );
+}
+
+1;
+
diff --git a/FS/FS/h_svc_acct.pm b/FS/FS/h_svc_acct.pm
index 247d20c..f525f82 100644
--- a/FS/FS/h_svc_acct.pm
+++ b/FS/FS/h_svc_acct.pm
@@ -1,16 +1,13 @@
 package FS::h_svc_acct;
+use base qw( FS::h_svc_Radius_Mixin FS::h_Common FS::svc_acct );
 
 use strict;
 use vars qw( @ISA $DEBUG );
 use Carp qw(carp);
 use FS::Record qw(qsearchs);
-use FS::h_Common;
-use FS::svc_acct;
 use FS::svc_domain;
 use FS::h_svc_domain;
 
- at ISA = qw( FS::h_Common FS::svc_acct );
-
 $DEBUG = 0;
 
 sub table { 'h_svc_acct' };
diff --git a/FS/FS/h_svc_broadband.pm b/FS/FS/h_svc_broadband.pm
index d6038fb..01477fe 100644
--- a/FS/FS/h_svc_broadband.pm
+++ b/FS/FS/h_svc_broadband.pm
@@ -1,11 +1,8 @@
 package FS::h_svc_broadband;
+use base qw( FS::h_svc_Radius_Mixin FS::h_Common FS::svc_broadband );
 
 use strict;
 use vars qw( @ISA );
-use FS::h_Common;
-use FS::svc_broadband;
-
- at ISA = qw( FS::h_Common FS::svc_broadband );
 
 sub table { 'h_svc_broadband' };
 
diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html
index 4b5df86..3a6a4d1 100755
--- a/httemplate/misc/cancel_pkg.html
+++ b/httemplate/misc/cancel_pkg.html
@@ -13,22 +13,47 @@
 % my $date_init = 0;
 % if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
 %   $submit =~ /^(\w*)\s/;
-<& /elements/tr-input-date-field.html, {
-    'name'    => 'date',
-    'value'   => $date,
-    'label'   => mt("$1 package on"),
-    'format'  => $date_format,
-} &>
+  <& /elements/tr-input-date-field.html, {
+      'name'    => 'date',
+      'value'   => $date,
+      'label'   => mt("$1 package on"),
+      'format'  => $date_format,
+  } &>
 %   $date_init = 1;
 % }
 
-% unless ( $method eq 'resume' ) { #the only one that doesn't need a reason
-<& /elements/tr-select-reason.html,
-     'field'          => 'reasonnum',
-     'reason_class'   => $class,
-     'curr_value'     => $reasonnum,
-     'control_button' => "document.getElementById('confirm_cancel_pkg_button')",
-&>
+% if ($method eq 'uncancel' ) {
+%
+% #XXX customer also requested setup
+% # setup: what usefulness is changing or blanking this?  re-charge setup fee?
+% #        an option that says that would be better if that's what we want to do
+
+% # last_bill: isn't this informational?  what good would editing it do?
+% #            something about invoice display?
+  <& /elements/tr-input-date-field.html, {
+      'name'    => 'last_bill',
+      'value'   => ( $cgi->param('last_bill') || $cust_pkg->get('last_bill') ),
+      'label'   => mt("Last bill date"),
+      'format'  => $date_format,
+  } &>
+
+  <& /elements/tr-input-date-field.html, {
+      'name'    => 'bill',
+      'value'   => ( $cgi->param('bill') || $cust_pkg->get('bill') ),
+      'label'   => mt("Next bill date"),
+      'format'  => $date_format,
+  } &>
+
+%   $date_init = 1;
+% }
+
+% unless ( $method eq 'resume' || $method eq 'uncancel' ) {
+  <& /elements/tr-select-reason.html,
+       field          => 'reasonnum',
+       reason_class   => $class,
+       curr_value     => $reasonnum,
+       control_button => "document.getElementById('confirm_cancel_pkg_button')",
+  &>
 % }
 
 % if ( ( $method eq 'adjourn' or $method eq 'suspend' ) and 
@@ -37,26 +62,27 @@
 %                     ? str2time($cgi->param('resume_date'))
 %                     : $cust_pkg->get('resume');
 
-<& /elements/tr-input-date-field.html, {
-    'name'    => 'resume_date',
-    'value'   => $resume_date,
-    'label'   => mt('Unsuspend on'),
-    'format'  => $date_format,
-    'noinit'  => $date_init,
-} &>
+  <& /elements/tr-input-date-field.html, {
+      'name'    => 'resume_date',
+      'value'   => $resume_date,
+      'label'   => mt('Unsuspend on'),
+      'format'  => $date_format,
+      'noinit'  => $date_init,
+  } &>
 % }
 </TABLE>
 
 <BR>
 <INPUT TYPE="submit" NAME="submit" ID="confirm_cancel_pkg_button" 
   VALUE="<% mt($submit) |h %>"
-  <% $method ne 'resume' ? 'DISABLED' : '' %>>
+  <% $method !~ /^(resume|uncancel)$/ ? 'DISABLED' : '' %>>
 
 </FORM>
 </BODY>
 </HTML>
 
 <%init>
+use Date::Parse qw(str2time);
 
 my $conf = new FS::Conf;
 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
@@ -99,6 +125,10 @@ if ($method eq 'cancel') {
   $class  = '';
   $submit = 'Unsuspend Later';
   $right  = 'Unsuspend customer package'; #later?
+} elsif ( $method eq 'uncancel') {
+  $class  = '';
+  $submit = 'Un-Cancel';
+  $right  = 'Un-cancel customer package'; #later?
 } else {
   die 'illegal query (unknown method param)';
 }
@@ -107,6 +137,7 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 die "access denied" unless $curuser->access_right($right);
 
 my $title = ucfirst($method) . ' Package';
+$title =~ s/Uncancel/Un-cancel/;
 
 my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum})
   or die "Unknown pkgnum: $pkgnum";
diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html
index 662a776..bc3a8cd 100755
--- a/httemplate/misc/process/cancel_pkg.html
+++ b/httemplate/misc/process/cancel_pkg.html
@@ -6,19 +6,21 @@
 </HTML>
 <%once>
 
-my %past = ( 'cancel'  => 'cancelled',
-             'expire'  => 'expired',
-             'suspend' => 'suspended',
-             'adjourn' => 'adjourned',
-             'resume'  => 'scheduled to resume',
+my %past = ( 'cancel'   => 'cancelled',
+             'expire'   => 'expired',
+             'suspend'  => 'suspended',
+             'adjourn'  => 'adjourned',
+             'resume'   => 'scheduled to resume',
+             'uncancel' => 'un-cancelled',
            );
 
 #i'm sure this is false laziness with somewhere, at least w/misc/cancel_pkg.html
-my %right = ( 'cancel'  => 'Cancel customer package immediately',
-              'expire'  => 'Cancel customer package later',
-              'suspend' => 'Suspend customer package',
-              'adjourn' => 'Suspend customer package later',
-              'resume'  => 'Unsuspend customer package', #later?
+my %right = ( 'cancel'   => 'Cancel customer package immediately',
+              'expire'   => 'Cancel customer package later',
+              'suspend'  => 'Suspend customer package',
+              'adjourn'  => 'Suspend customer package later',
+              'resume'   => 'Unsuspend customer package', #later?
+              'uncancel' => 'Un-cancel customer package',
             );
 
 </%once>
@@ -26,7 +28,8 @@ my %right = ( 'cancel'  => 'Cancel customer package immediately',
 
 #untaint method
 my $method = $cgi->param('method');
-$method =~ /^(cancel|expire|suspend|adjourn|resume)$/ or die "Illegal method";
+$method =~ /^(cancel|expire|suspend|adjourn|resume|uncancel)$/
+  or die "Illegal method";
 $method = $1;
 my $past_method = $past{$method};
 
@@ -39,7 +42,7 @@ $pkgnum =~ /^(\d+)$/ or die "Illegal pkgnum";
 $pkgnum = $1;
 
 my $date = time;
-if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume'){
+if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
   #untaint date
   $date = $cgi->param('date'); #huh?
   parse_datetime($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date";
@@ -59,7 +62,7 @@ my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
 
 #untaint reasonnum
 my $reasonnum = $cgi->param('reasonnum');
-if ( $method ne 'unsuspend' ) { #i.e. 'resume'
+if ( $method !~ /^(unsuspend|uncancel)$/ ) {
   $reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
   $reasonnum = $1;
 
@@ -71,9 +74,18 @@ if ( $method ne 'unsuspend' ) { #i.e. 'resume'
   }
 }
 
+#for uncancel
+my $last_bill =
+  $cgi->param('last_bill') ? parse_datetime($cgi->param('last_bill')) : '';
+my $bill =
+  $cgi->param('bill')      ? parse_datetime($cgi->param('bill'))      : '';
+
 my $error = $cust_pkg->$method( 'reason'      => $reasonnum,
                                 'date'        => $date,
-                                'resume_date' => $resume_date );
+                                'resume_date' => $resume_date,
+                                'last_bill'   => $last_bill,
+                                'bill'        => $bill,
+                              );
 
 if ($error) {
   $cgi->param('error', $error);
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
index 5f458e6..c0a56d0 100644
--- a/httemplate/view/cust_main/packages/services.html
+++ b/httemplate/view/cust_main/packages/services.html
@@ -3,7 +3,7 @@
 % ###
 
   <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
-    <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+    <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=2 WIDTH="100%">
     <SCRIPT TYPE="text/javascript">
 function clearhint_search_cust_svc(obj, str) {
   if (obj.value == str) obj.value = '';
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 28df9da..4aec90e 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -32,7 +32,20 @@
 
 %   } 
 %
-% } else { 
+%   if ( $part_pkg->freq ) { #?
+
+      <TR>
+        <TD COLSPAN=<%$colspan%>>
+          <FONT SIZE=-1>
+%           if ( $curuser->access_right('Un-cancel customer package') ) { 
+              ( <% pkg_uncancel_link($cust_pkg) %> )
+%           } 
+          <FONT>
+        </TD>
+      </TR>
+%   }
+%
+% } else {
 %
 %   if ( $cust_pkg->get('susp') ) { #status: suspended
 %     my $cpr = $cust_pkg->last_cust_pkg_reason('susp');
@@ -56,6 +69,8 @@
       <% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt ) %>
 %   } 
 
+    <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
     <% pkg_status_row_changed( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
     <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
 %   if ( $part_pkg->option('suspend_bill', 1) ) {
@@ -99,6 +114,8 @@
              )
           %>
 
+          <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
           <TR>
             <TD COLSPAN=<%$colspan%>>
               <FONT SIZE=-1>
@@ -118,6 +135,7 @@
           <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
 
           <% pkg_status_row_if($cust_pkg, emt('Start billing'), 'start_date', %opt) %>
+          <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
 
 %       } 
 %
@@ -133,6 +151,8 @@
 
           <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
 
+          <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
 %       } else { 
 %
 %         my $num_cust_svc = $cust_pkg->num_cust_svc;
@@ -166,6 +186,8 @@
 
           <% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt) %>
 
+          <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
 %       } 
 %
 %     } 
@@ -467,6 +489,16 @@ sub pkg_cancel_link {
          )
 }
 
+sub pkg_uncancel_link {
+  include( '/elements/popup_link-cust_pkg.html',
+             'action'      => $p. 'misc/cancel_pkg.html?method=uncancel',
+             'label'       => emt('Un-cancel'),
+             'actionlabel' => emt('Un-cancel'),
+             #'color'       =>  #?
+             'cust_pkg'    => shift,
+         )
+}
+
 sub pkg_expire_link {
   include( '/elements/popup_link-cust_pkg.html',
              'action'      => $p. 'misc/cancel_pkg.html?method=expire',

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

Summary of changes:
 FS/FS/AccessRight.pm                             |    1 +
 FS/FS/Schema.pm                                  |    2 +
 FS/FS/access_right.pm                            |    1 +
 FS/FS/cust_pkg.pm                                |  139 +++++++++++++++++++++-
 FS/FS/h_radius_usergroup.pm                      |   24 ++++
 FS/FS/h_svc_Radius_Mixin.pm                      |   17 +++
 FS/FS/h_svc_acct.pm                              |    5 +-
 FS/FS/h_svc_broadband.pm                         |    5 +-
 httemplate/misc/cancel_pkg.html                  |   73 ++++++++----
 httemplate/misc/process/cancel_pkg.html          |   40 ++++--
 httemplate/view/cust_main/packages/services.html |    2 +-
 httemplate/view/cust_main/packages/status.html   |   34 +++++-
 12 files changed, 297 insertions(+), 46 deletions(-)
 create mode 100644 FS/FS/h_radius_usergroup.pm
 create mode 100644 FS/FS/h_svc_Radius_Mixin.pm




More information about the freeside-commits mailing list