RT#24684: Payments for Online Bill Pay [Credit Balance Display]
[freeside.git] / FS / FS / ClientAPI / MyAccount.pm
index c57584e..420ed06 100644 (file)
@@ -23,7 +23,7 @@ use FS::Conf;
 #use FS::UID qw(dbh);
 use FS::Record qw(qsearch qsearchs dbh);
 use FS::Msgcat qw(gettext);
-use FS::Misc qw(card_types);
+use FS::Misc qw(card_types money_pretty);
 use FS::Misc::DateTime qw(parse_datetime);
 use FS::TicketSystem;
 use FS::ClientAPI_SessionCache;
@@ -48,6 +48,7 @@ use FS::msg_template;
 use FS::contact;
 use FS::cust_contact;
 use FS::cust_location;
+use FS::cust_payby;
 
 use FS::ClientAPI::MyAccount::quotation; # just for code organization
 
@@ -130,7 +131,7 @@ sub skin_info {
       ),
       'menu_disable' => [ $conf->config('selfservice-menu_disable',$agentnum) ],
       ( map { $_ => $conf->exists("selfservice-$_", $agentnum ) }
-        qw( menu_skipblanks menu_skipheadings menu_nounderline no_logo )
+        qw( menu_skipblanks menu_skipheadings menu_nounderline no_logo enable_payment_without_balance )
       ),
       ( map { $_ => scalar($conf->config_binary("selfservice-$_", $agentnum)) }
         qw( title_left_image title_right_image
@@ -260,16 +261,39 @@ sub login {
     my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
       or return { error => 'Domain '. $p->{'domain'}. ' not found' };
 
-    my $svc_acct = qsearchs( 'svc_acct', { 'username'  => $p->{'username'},
-                                           'domsvc'    => $svc_domain->svcnum, }
-                           );
-    return { error => 'User not found.' } unless $svc_acct;
+    my @svc_acct = qsearch( 'svc_acct', { 'username'  => $p->{'username'},
+                                          'domsvc'    => $svc_domain->svcnum, }
+                          );
+
+    if ( $conf->exists('selfservice_server-login_svcpart') ) {
+      my @svcpart = $conf->config('selfservice_server-login_svcpart');
+      @svc_acct = grep { my $svcpart = $_->cust_svc->svcpart;
+                         scalar( grep( $_ eq $svcpart, @svcpart ) );
+                       }
+                    @svc_acct;
+    }
 
-    if($conf->exists('selfservice_server-login_svcpart')) {
-       my @svcpart = $conf->config('selfservice_server-login_svcpart');
-       my $svcpart = $svc_acct->cust_svc->svcpart;
-       return { error => 'Invalid user.' } 
-           unless grep($_ eq $svcpart, @svcpart);
+    if ( $conf->exists('selfservice_server-primary_only') ) {
+        @svc_acct =
+          grep {
+            my $cust_svc = $_->cust_svc;
+            $cust_svc->cust_pkg->part_pkg->svcpart([qw( svc_acct svc_phone )])
+              == $cust_svc->svcpart
+          }
+          @svc_acct;
+    }
+
+    return { error => 'User not found.' } unless @svc_acct;
+
+    return { error => 'Multiple users.' } if scalar(@svc_acct) > 1;
+
+    my $svc_acct = $svc_acct[0];
+
+    if ( $conf->exists('selfservice_server-login_svcpart') ) {
+      my @svcpart = $conf->config('selfservice_server-login_svcpart');
+      my $svcpart = $svc_acct->cust_svc->svcpart;
+      return { error => 'Invalid user.' } 
+        unless grep($_ eq $svcpart, @svcpart);
     }
 
     return { error => 'Incorrect password.' }
@@ -473,11 +497,13 @@ sub customer_info {
     if ( $session->{'pkgnum'} ) {
       #XXX open invoices in the pkg-balances case
     } else {
+      $return{'money_char'} = $conf->config("money_char") || '$';
       my @open = map {
                        {
-                         invnum => $_->invnum,
-                         date   => time2str("%b %o, %Y", $_->_date),
-                         owed   => $_->owed,
+                         invnum     => $_->invnum,
+                         date       => time2str("%b %o, %Y", $_->_date),
+                         owed       => $_->owed,
+                         charged    => $_->charged,
                        };
                      } $cust_main->open_cust_bill;
       $return{open_invoices} = \@open;
@@ -583,6 +609,7 @@ sub customer_info_short {
         $return{next_bill_date} ? time2str('%m/%d/%Y', $return{next_bill_date} )
                                 : '(none)';
     }
+    $return{balance_pretty} = money_pretty($return{balance});
 
     $return{countrydefault} = scalar($conf->config('countrydefault'));
 
@@ -666,78 +693,22 @@ sub billing_history {
   }
 
   $return{balance} = $cust_main->balance;
+  $return{balance_pretty} = money_pretty($return{balance});
   $return{next_bill_date} = $cust_main->next_bill_date;
   $return{next_bill_date_pretty} =
     $return{next_bill_date} ? time2str('%m/%d/%Y', $return{next_bill_date} )
                             : '(none)';
 
-  my @history = ();
-
   my $conf = new FS::Conf;
 
-  if ( $conf->exists('selfservice-billing_history-line_items') ) {
-
-    foreach my $cust_bill ( $cust_main->cust_bill ) {
-
-      push @history, {
-        'type'        => 'Line item',
-        'description' => $_->desc( $cust_main->locale ).
-                           ( $_->sdate && $_->edate
-                               ? ' '. time2str('%d-%b-%Y', $_->sdate).
-                                 ' To '. time2str('%d-%b-%Y', $_->edate)
-                               : ''
-                           ),
-        'amount'      => sprintf('%.2f', $_->setup + $_->recur ),
-        'date'        => $cust_bill->_date,
-        'date_pretty' =>  time2str('%m/%d/%Y', $cust_bill->_date ),
-      }
-        foreach $cust_bill->cust_bill_pkg;
-
-    }
-
-  } else {
+  $return{'history'} = [
+    $cust_main->payment_history(
+      'line_items' => $conf->exists('selfservice-billing_history-line_items'),
+      'reverse_sort' => 1,
+    )
+  ];
 
-    push @history, {
-                     'type'        => 'Invoice',
-                     'description' => 'Invoice #'. $_->display_invnum,
-                     'amount'      => sprintf('%.2f', $_->charged ),
-                     'date'        => $_->_date,
-                     'date_pretty' =>  time2str('%m/%d/%Y', $_->_date ),
-                   }
-      foreach $cust_main->cust_bill;
-
-  }
-
-  push @history, {
-                   'type'        => 'Payment',
-                   'description' => 'Payment', #XXX type
-                   'amount'      => sprintf('%.2f', 0 - $_->paid ),
-                   'date'        => $_->_date,
-                   'date_pretty' =>  time2str('%m/%d/%Y', $_->_date ),
-                 }
-    foreach $cust_main->cust_pay;
-
-  push @history, {
-                   'type'        => 'Credit',
-                   'description' => 'Credit', #more info?
-                   'amount'      => sprintf('%.2f', 0 -$_->amount ),
-                   'date'        => $_->_date,
-                   'date_pretty' =>  time2str('%m/%d/%Y', $_->_date ),
-                 }
-    foreach $cust_main->cust_credit;
-
-  push @history, {
-                   'type'        => 'Refund',
-                   'description' => 'Refund', #more info?  type, like payment?
-                   'amount'      => $_->refund,
-                   'date'        => $_->_date,
-                   'date_pretty' =>  time2str('%m/%d/%Y', $_->_date ),
-                 }
-    foreach $cust_main->cust_refund;
-
-  @history = sort { $b->{'date'} <=> $a->{'date'} } @history;
-
-  $return{'history'} = \@history;
+  $return{'money_char'} = $conf->config("money_char") || '$',
 
   return \%return;
 
@@ -796,16 +767,16 @@ sub edit_info {
 
     if ( $new->payinfo eq $cust_main->paymask ) {
       $new->payinfo($cust_main->payinfo);
+      $new->paycvv( $p->{'paycvv'} || $cust_main->paycvv );
     } else {
       $new->payinfo($p->{'payinfo'});
+      return { 'error' => 'CVV2 is required' }
+        if ! $p->{'paycvv'} && $conf->exists('selfservice-onfile_require_cvv');
+      $new->paycvv( $p->{'paycvv'} )
     }
 
     $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
 
-    if ( $conf->exists('selfservice-onfile_require_cvv') ){
-      return { 'error' => 'CVV2 is required' } unless $p->{'paycvv'};
-    }
-
   } elsif ( $payby =~ /^(CHEK|DCHK)$/ ) {
 
     my $payinfo;
@@ -886,7 +857,7 @@ sub payment_info {
       'require_cvv'        => $conf->exists('selfservice-require_cvv'),
       'onfile_require_cvv' => $conf->exists('selfservice-onfile_require_cvv'),
 
-      'paytypes' => [ @FS::cust_main::paytypes ],
+      'paytypes' => [ FS::cust_payby::paytypes ],
 
       'paybys' => [ $conf->config('signup_server-payby') ],
       'cust_paybys' => \@cust_paybys,
@@ -1589,25 +1560,31 @@ sub list_invoices {
   my @cust_bill = grep ! $_->hide, $cust_main->cust_bill;
 
   my $balance = 0;
+  my $invoices = [
+    map {
+      #not super efficient, we also run cust_bill_pay/cust_credited inside owed
+      my @payments_and_credits = sort {$b->_date <=> $a->_date} ($_->cust_bill_pay,$_->cust_credited);
+      my $owed = $_->owed;
+      $balance += $owed;
+      +{ 'invnum'       => $_->invnum,
+         '_date'        => $_->_date,
+         'date'         => time2str("%b %o, %Y", $_->_date),
+         'date_short'   => time2str("%m-%d-%Y",  $_->_date),
+         'previous'     => sprintf('%.2f', ($_->previous)[0]),
+         'charged'      => sprintf('%.2f', $_->charged),
+         'owed'         => sprintf('%.2f', $owed),
+         'balance'      => sprintf('%.2f', $balance),
+         'lastpay'      => @payments_and_credits 
+                           ? time2str("%b %o, %Y", $payments_and_credits[0]->_date)
+                           : '',
+      }
+    } @cust_bill
+  ];
 
   return  { 'error'       => '',
             'balance'     => $cust_main->balance,
-            'invoices'    => [
-              map {
-                    my $owed = $_->owed;
-                    $balance += $owed;
-                    +{ 'invnum'       => $_->invnum,
-                       '_date'        => $_->_date,
-                       'date'         => time2str("%b %o, %Y", $_->_date),
-                       'date_short'   => time2str("%m-%d-%Y",  $_->_date),
-                       'previous'     => sprintf('%.2f', ($_->previous)[0]),
-                       'charged'      => sprintf('%.2f', $_->charged),
-                       'owed'         => sprintf('%.2f', $owed),
-                       'balance'      => sprintf('%.2f', $balance),
-                     }
-                  }
-                  @cust_bill
-            ],
+            'money_char'  => $conf->config("money_char") || '$',
+            'invoices'    => $invoices,
             'legacy_invoices' => [
               map {
                     +{ 'legacyinvnum' => $_->legacyinvnum,