more self-servicey stuff (change package, detailed usage)
authorjeff <jeff>
Fri, 5 Jan 2007 05:19:35 +0000 (05:19 +0000)
committerjeff <jeff>
Fri, 5 Jan 2007 05:19:35 +0000 (05:19 +0000)
FS/FS/ClientAPI/MyAccount.pm
fs_selfservice/FS-SelfService/SelfService.pm
fs_selfservice/FS-SelfService/cgi/change_pkg.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/process_change_pkg.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/provision_list.html
fs_selfservice/FS-SelfService/cgi/selfservice.cgi
fs_selfservice/FS-SelfService/cgi/view_usage.html
fs_selfservice/FS-SelfService/cgi/view_usage_details.html [new file with mode: 0644]

index dc4ae78..8b6a466 100644 (file)
@@ -24,6 +24,14 @@ use FS::cust_main_county;
 use FS::cust_pkg;
 use HTML::Entities;
 
+#false laziness with FS::cust_main
+BEGIN {
+  eval "use Time::Local;";
+  die "Time::Local minimum version 1.05 required with Perl versions before 5.6"
+    if $] < 5.006 && !defined($Time::Local::VERSION);
+  eval "use Time::Local qw(timelocal_nocheck);";
+}
+
 use vars qw( @cust_main_editable_fields );
 @cust_main_editable_fields = qw(
   first last company address1 address2 city
@@ -538,8 +546,6 @@ sub list_pkgs {
 sub list_svcs {
   my $p = shift;
 
-  use Data::Dumper;
-
   my($context, $session, $custnum) = _custoragent_session_custnum($p);
   return { 'error' => $session } if $context eq 'error';
 
@@ -592,6 +598,85 @@ sub list_svcs {
 
 }
 
+sub list_svc_usage {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+
+  my $search = { 'svcnum' => $p->{'svcnum'} };
+  $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+  my $svc_acct = qsearchs ( 'svc_acct', $search );
+  return { 'error' => 'No service selected in list_svc_usage' } 
+    unless $svc_acct;
+
+  my $freq   = $svc_acct->cust_svc->cust_pkg->part_pkg->freq;
+  my $start  = $svc_acct->cust_svc->cust_pkg->setup;
+  my $end    = $svc_acct->cust_svc->cust_pkg->bill; # or time?
+
+  unless($p->{beginning}){
+    $p->{beginning} = $svc_acct->cust_svc->cust_pkg->last_bill;
+    $p->{ending} = $end;
+  }
+  my @usage = ();
+
+  foreach my $part_export ( 
+    map { qsearch ( 'part_export', { 'exporttype' => $_ } ) }
+    qw (sqlradius sqlradius_withdomain')
+  ) {
+
+    push @usage, @ { $part_export->usage_sessions($p->{beginning},
+                                                  $p->{ending},
+                                                  $svc_acct)
+                   };
+  }
+
+  #kinda false laziness with FS::cust_main::bill, but perhaps
+  #we should really change this bit to DateTime and DateTime::Duration
+  #
+  #change this bit to use Date::Manip? CAREFUL with timezones (see
+  # mailing list archive)
+  my ($nsec,$nmin,$nhour,$nmday,$nmon,$nyear) =
+    (localtime($p->{ending}) )[0,1,2,3,4,5];
+  my ($psec,$pmin,$phour,$pmday,$pmon,$pyear) =
+    (localtime($p->{beginning}) )[0,1,2,3,4,5];
+
+  if ( $freq =~ /^\d+$/ ) {
+    $nmon += $freq;
+    until ( $nmon < 12 ) { $nmon -= 12; $nyear++; }
+    $pmon -= $freq;
+    until ( $pmon >= 0 ) { $pmon += 12; $pyear--; }
+  } elsif ( $freq =~ /^(\d+)w$/ ) {
+    my $weeks = $1;
+    $nmday += $weeks * 7;
+    $pmday -= $weeks * 7;
+  } elsif ( $freq =~ /^(\d+)d$/ ) {
+    my $days = $1;
+    $nmday += $days;
+    $pmday -= $days;
+  } elsif ( $freq =~ /^(\d+)h$/ ) {
+    my $hours = $1;
+    $nhour += $hours;
+    $phour -= $hours;
+  } else {
+    return { 'error' => "unparsable frequency: ". $freq };
+  }
+  
+  my $previous  = timelocal_nocheck($psec,$pmin,$phour,$pmday,$pmon,$pyear);
+  my $next      = timelocal_nocheck($nsec,$nmin,$nhour,$nmday,$nmon,$nyear);
+
+
+  { 
+    'error'     => '',
+    'svcnum'    => $p->{svcnum},
+    'beginning' => $p->{beginning},
+    'ending'    => $p->{ending},
+    'previous'  => ($previous > $start) ? $previous : $start,
+    'next'      => ($next < $end) ? $next : $end,
+    'usage'     => \@usage,
+  };
+}
+
 sub order_pkg {
   my $p = shift;
 
@@ -674,23 +759,11 @@ sub order_pkg {
   my $conf = new FS::Conf;
   if ( $conf->exists('signup_server-realtime') ) {
 
-    my $old_balance = $cust_main->balance;
-
-    my $bill_error = $cust_main->bill;
-    $cust_main->apply_payments;
-    $cust_main->apply_credits;
-    $bill_error = $cust_main->collect('realtime' => 1);
-
-    if (    $cust_main->balance > $old_balance
-         && $cust_main->balance > 0
-         && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) {
-      #this makes sense.  credit is "un-doing" the invoice
-      $cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ),
-                          'self-service decline' );
-      $cust_main->apply_credits( 'order' => 'newest' );
+    my $bill_error = _do_bop_realtime( $cust_main );
 
+    if ($bill_error) {
       $cust_pkg->cancel('quiet'=>1);
-      return { 'error' => '_decline', 'bill_error' => $bill_error };
+      return $bill_error;
     } else {
       $cust_pkg->reexport;
     }
@@ -703,6 +776,47 @@ sub order_pkg {
 
 }
 
+sub change_pkg {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+
+  my $search = { 'custnum' => $custnum };
+  $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+  my $cust_main = qsearchs('cust_main', $search )
+    or return { 'error' => "unknown custnum $custnum" };
+
+  my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $p->{pkgnum} } )
+    or return { 'error' => "unknown package $p->{pkgnum}" };
+
+  my @newpkg;
+  my $error = FS::cust_pkg::order( $custnum,
+                                   [$p->{pkgpart}],
+                                   [$p->{pkgnum}],
+                                   \@newpkg,
+                                 );
+
+  my $conf = new FS::Conf;
+  if ( $conf->exists('signup_server-realtime') ) {
+
+    my $bill_error = _do_bop_realtime( $cust_main );
+
+    if ($bill_error) {
+      $newpkg[0]->suspend;
+      return $bill_error;
+    } else {
+      $newpkg[0]->reexport;
+    }
+
+  } else {  
+    $newpkg[0]->reexport;
+  }
+
+  return { error => '', pkgnum => $cust_pkg->pkgnum };
+
+}
+
 sub order_recharge {
   my $p = shift;
 
@@ -726,8 +840,6 @@ sub order_recharge {
          recharge_totalbytes );
   my $amount = $part_pkg->option('recharge_amount', 1); 
   
-  my $old_balance = $cust_main->balance;
-
   my ($l, $v, $d) = $cust_svc->label;  # blah
   my $pkg = "Recharge $v"; 
 
@@ -739,13 +851,36 @@ sub order_recharge {
   my $conf = new FS::Conf;
   if ( $conf->exists('signup_server-realtime') && !$bill_error ) {
 
-    $bill_error = $cust_main->bill;
+    $bill_error = _do_bop_realtime( $cust_main );
+
+    if ('bill_error') {
+      return $bill_error;
+    } else {
+      my $error = $svc_x->recharge (\%vhash);
+      return { 'error' => $error } if $error;
+    }
+
+  } else {  
+    my $error = $bill_error;
+    $error ||= $svc_x->recharge (\%vhash);
+    return { 'error' => $error } if $error;
+  }
+
+  return { error => '', svc => $cust_svc->part_svc->svc };
+
+}
+
+sub _do_bop_realtime {
+  my ($cust_main) = @_;
+
+    my $old_balance = $cust_main->balance;
+
+    my $bill_error = $cust_main->bill;
 
     $cust_main->apply_payments;
     $cust_main->apply_credits;
     $bill_error = $cust_main->collect('realtime' => 1);
 
-    #false laziness with order_pkg
     if (    $cust_main->balance > $old_balance
          && $cust_main->balance > 0
          && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) {
@@ -754,20 +889,10 @@ sub order_recharge {
                           'self-service decline' );
       $cust_main->apply_credits( 'order' => 'newest' );
 
-      return { 'error' => '_decline', 'bill_error' => encode_entities($bill_error) };
-    } else {
-      my $error = $svc_x->recharge (\%vhash);
-      return { 'error' => $error } if $error;
+      return { 'error' => '_decline', 'bill_error' => $bill_error };
     }
 
-  } else {  
-    my $error = $bill_error;
-    $error ||= $svc_x->recharge (\%vhash);
-    return { 'error' => $error } if $error;
-  }
-
-  return { error => '', svc => $cust_svc->part_svc->svc };
-
+    '';
 }
 
 sub cancel_pkg {
index 980c899..fe17fae 100644 (file)
@@ -37,7 +37,9 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   'process_prepay'       => 'MyAccount/process_prepay',
   'list_pkgs'            => 'MyAccount/list_pkgs',     #add to ss cgi (added?)
   'list_svcs'            => 'MyAccount/list_svcs',     #add to ss cgi (added?)
+  'list_svc_usage'       => 'MyAccount/list_svc_usage',   
   'order_pkg'            => 'MyAccount/order_pkg',     #add to ss cgi!
+  'change_pkg'           => 'MyAccount/change_pkg', 
   'order_recharge'       => 'MyAccount/order_recharge',
   'cancel_pkg'           => 'MyAccount/cancel_pkg',    #add to ss cgi!
   'charge'               => 'MyAccount/charge',        #?
diff --git a/fs_selfservice/FS-SelfService/cgi/change_pkg.html b/fs_selfservice/FS-SelfService/cgi/change_pkg.html
new file mode 100644 (file)
index 0000000..a841308
--- /dev/null
@@ -0,0 +1,37 @@
+<SCRIPT TYPE="text/javascript">
+function enable_change_pkg () {
+  if ( document.ChangePkgForm.pkgpart.selectedIndex > 0 ) {
+    document.ChangePkgForm.submit.disabled = false;
+  } else {
+    document.ChangePkgForm.submit.disabled = true;
+  }
+}
+</SCRIPT>
+<FONT SIZE=4>Purchase replacement package for "<%= $pkg; %>"</FONT><BR><BR>
+<%= if ( $error ) {
+  $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+<FORM NAME="ChangePkgForm" ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_change_pkg">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="pkg" VALUE="<%= $pkg %>">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+<TR>
+  <TD COLSPAN=2><SELECT NAME="pkgpart" onChange="enable_change_pkg()">
+  <OPTION VALUE="">
+
+  <%=
+    foreach my $part_pkg ( @part_pkg ) {
+      $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+      $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+      $OUT .= '>'. $part_pkg->{'pkg'};
+    }
+  %>
+
+  </SELECT></TD>
+</TR>
+</TABLE>
+<INPUT NAME="submit" TYPE="submit" VALUE="Purchase" disabled>
+</FORM>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html b/fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html
new file mode 100644 (file)
index 0000000..d08ab96
--- /dev/null
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('change_pkg') %>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_pkg.html b/fs_selfservice/FS-SelfService/cgi/process_change_pkg.html
new file mode 100644 (file)
index 0000000..9347434
--- /dev/null
@@ -0,0 +1,13 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Package change successful.</FONT>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
index cd587f0..88d1c84 100644 (file)
@@ -16,9 +16,11 @@ function areyousure(href, message) {
     ) {
 
   $OUT .= #'<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#ffffff">'.
-          '<TR><TH BGCOLOR="#6666ff" COLSPAN=3>'.
-          $pkg->{'pkg'}.
-          '</TH></TR>';
+          '<TR><TH BGCOLOR="#6666ff" COLSPAN=2>'.
+          $pkg->{'pkg'}. '</TH><TH BGCOLOR="#6666ff" >' .
+          qq!(<A style="font-size: smaller;color: #000000" HREF="! .
+          qq!${url}customer_change_pkg;pkgnum=$pkg->{'pkgnum'};pkg=$pkg->{'pkg'}">! .
+          'change</A>)</TH></TR>';
 
   my $col1 = "ffffff";
   my $col2 = "dddddd";
index e2fd7f6..1fc2e5f 100644 (file)
@@ -7,13 +7,14 @@ use CGI;
 use CGI::Carp qw(fatalsToBrowser);
 use Text::Template;
 use HTML::Entities;
+use Date::Format;
 use FS::SelfService qw( login customer_info invoice
                         payment_info process_payment 
                         process_prepay
                         list_pkgs order_pkg signup_info order_recharge
                         part_svc_info provision_acct provision_external
-                        unprovision_svc
-                        list_svcs myaccount_passwd
+                        unprovision_svc change_pkg
+                        list_svcs list_svc_usage myaccount_passwd
                       );
 
 $template_dir = '.';
@@ -65,7 +66,7 @@ $session_id = $cgi->param('session');
 
 #order|pw_list XXX ???
 $cgi->param('action') =~
-    /^(myaccount|view_invoice|make_payment|payment_results|recharge_prepay|recharge_results|logout|change_bill|change_ship|customer_order_pkg|process_order_pkg|process_order_recharge|provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|view_usage||change_password|process_change_password)$/
+    /^(myaccount|view_invoice|make_payment|payment_results|recharge_prepay|recharge_results|logout|change_bill|change_ship|customer_order_pkg|process_order_pkg|customer_change_pkg|process_change_pkg|process_order_recharge|provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|view_usage|view_usage_details|change_password|process_change_password)$/
   or die "unknown action ". $cgi->param('action');
 my $action = $1;
 
@@ -123,6 +124,24 @@ sub customer_order_pkg {
   };
 }
 
+sub customer_change_pkg {
+  my $init_data = signup_info( 'customer_session_id' => $session_id );
+  return $init_data if ( $init_data->{'error'} );
+
+  my $customer_info = customer_info( 'session_id' => $session_id );
+  return $customer_info if ( $customer_info->{'error'} );
+
+  return {
+    ( map { $_ => $init_data->{$_} }
+          qw( part_pkg security_phrase svc_acct_pop ),
+    ),
+    ( map { $_ => $cgi->param($_) }
+        qw( pkgnum pkg )
+    ),
+    %$customer_info,
+  };
+}
+
 sub process_order_pkg {
 
   my $results = '';
@@ -160,6 +179,30 @@ sub process_order_pkg {
 
 }
 
+sub process_change_pkg {
+
+  my $results = '';
+
+  $results ||= change_pkg (
+    'session_id' => $session_id,
+    map { $_ => $cgi->param($_) }
+        qw( pkgpart pkgnum )
+  );
+
+
+  if ( $results->{'error'} ) {
+    $action = 'customer_change_pkg';
+    return {
+      $cgi->Vars,
+      %{customer_change_pkg()},
+      'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+    };
+  } else {
+    return $results;
+  }
+
+}
+
 sub process_order_recharge {
 
   my $results = '';
@@ -356,6 +399,15 @@ sub view_usage {
   );
 }
 
+sub view_usage_details {
+  list_svc_usage(
+    'session_id'  => $session_id,
+    'svcnum'      => $cgi->param('svcnum'),
+    'beginning'   => $cgi->param('beginning') || '',
+    'ending'      => $cgi->param('ending') || '',
+  );
+}
+
 sub change_password {
   list_svcs(
     'session_id' => $session_id,
index 73ad1e4..79d07d4 100644 (file)
     <TH ALIGN="right">Total remaining</TH>
   </TR>
 <%= foreach my $svc ( @svcs ) {
+      my $link = "${url}view_usage_details;".
+        "svcnum=$svc->{'svcnum'};beginning=0;ending=0";
   $OUT .= '<TR><TD>';
-    $OUT .= $svc->{'label'}. ': '. $svc->{'value'};
+    $OUT .= qq!<A HREF="$link">!. $svc->{'label'}. ': '. $svc->{'value'}.'</A>';
     $OUT .= '</TD><TD ALIGN="right">';
     $OUT .= $svc->{'seconds'};
     $OUT .= '</TD><TD ALIGN="right">';
diff --git a/fs_selfservice/FS-SelfService/cgi/view_usage_details.html b/fs_selfservice/FS-SelfService/cgi/view_usage_details.html
new file mode 100644 (file)
index 0000000..9067755
--- /dev/null
@@ -0,0 +1,66 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Service usage details</FONT><BR><BR>
+
+<%= if ( $error ) {
+  $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+<TABLE WIDTH="100%">
+  <TR>
+    <TD WIDTH="50%">
+<%= if ($previous < $beginning) {
+    $OUT .= qq!<A HREF="${url}view_usage_details;svcnum=$svcnum;beginning=!;
+    $OUT .= qq!$previous;ending=$beginning">Previous period</A>!;
+    }else{
+      '';
+    } %>
+    </TD>
+    <TD  WIDTH="50%" ALIGN="right">
+<%= if ($next > $ending) {
+    $OUT .= qq!<A HREF="${url}view_usage_details;svcnum=$svcnum;beginning=!;
+    $OUT .= qq!$ending;ending=$next">Next period</A>!;
+    }else{
+      '';
+    }%>
+    </TD>
+  </TR>
+</TABLE>
+<TABLE BGCOLOR="#cccccc">
+  <TR>
+    <TH ALIGN="left">Account</TH>
+    <TH ALIGN="right">Start Time</TH>
+    <TH ALIGN="right">Duration</TH>
+  </TR>
+<%= my $total = 0;
+    foreach my $usage ( @usage ) {
+  $OUT .= '<TR><TD>';
+    $OUT .= $usage->{'username'};
+    $OUT .= '</TD><TD ALIGN="right">';
+    $OUT .= Date::Format::time2str('%T%P %a&nbsp;%b&nbsp;%o&nbsp;%Y', $usage->{'acctstarttime'});
+    $OUT .= '</TD><TD ALIGN="right">';
+    my $duration =  $usage->{'acctstoptime'} - $usage->{'acctstarttime'};
+    $total += $duration;
+    my $h = int($duration/3600);
+    my $m = sprintf("%02d", int(($duration % 3600) / 60));
+    my $s = sprintf("%02d", $duration % 60);
+    $OUT .=  "$h:$m:$s";
+  $OUT .= '</TD></TR>';
+  }
+  my $h = int($total/3600);
+  my $m = sprintf("%02d", int(($total % 3600) / 60));
+  my $s = sprintf("%02d", $total % 60);
+  $OUT .=  qq!<TR><TD></TD><TD></TD><TD ALIGN="right">========</TD></TR>!;
+  $OUT .=  qq!<TR><TD></TD><TD></TD><TD ALIGN="right">$h:$m:$s</TD></TR>!; %>
+
+</TABLE>
+<BR>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>