add signup-duplicate_cc-warn_hours to warn about duplicate signups in a time span...
[freeside.git] / FS / FS / cust_main.pm
index d95ac70..d0a071c 100644 (file)
@@ -3,7 +3,7 @@ package FS::cust_main;
 require 5.006;
 use strict;
              #FS::cust_main:_Marketgear when they're ready to move to 2.1
-use base qw( FS::cust_main::Packages
+use base qw( FS::cust_main::Packages FS::cust_main::Status
              FS::cust_main::Billing FS::cust_main::Billing_Realtime
              FS::cust_main::Billing_Discount
              FS::otaker_Mixin FS::payinfo_Mixin FS::cust_main_Mixin
@@ -743,7 +743,7 @@ sub get_prepay {
 
     $prepay_credit = qsearchs(
       'prepay_credit',
-      { 'identifier' => $prepay_credit },
+      { 'identifier' => $identifier },
       '',
       'FOR UPDATE'
     );
@@ -1652,6 +1652,7 @@ sub check {
     || $self->ut_alphan('geocode')
     || $self->ut_floatn('cdr_termination_percentage')
     || $self->ut_floatn('credit_limit')
+    || $self->ut_numbern('billday')
   ;
 
   #barf.  need message catalogs.  i18n.  etc.
@@ -1830,12 +1831,17 @@ sub check {
       && cardtype($self->payinfo) eq "Unknown";
 
     unless ( $ignore_banned_card ) {
-      my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+      my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } );
       if ( $ban ) {
-        return 'Banned credit card: banned on '.
-               time2str('%a %h %o at %r', $ban->_date).
-               ' by '. $ban->otaker.
-               ' (ban# '. $ban->bannum. ')';
+        if ( $ban->bantype eq 'warn' ) {
+          #or others depending on value of $ban->reason ?
+          return '_duplicate_card' unless $self->override_ban_warn;
+        } else {
+          return 'Banned credit card: banned on '.
+                 time2str('%a %h %o at %r', $ban->_date).
+                 ' by '. $ban->otaker.
+                 ' (ban# '. $ban->bannum. ')';
+        }
       }
     }
 
@@ -1880,8 +1886,12 @@ sub check {
   } elsif ( $check_payinfo && $self->payby =~ /^(CHEK|DCHK)$/ ) {
 
     my $payinfo = $self->payinfo;
-    $payinfo =~ s/[^\d\@]//g;
-    if ( $conf->exists('echeck-nonus') ) {
+    $payinfo =~ s/[^\d\@\.]//g;
+    if ( $conf->exists('cust_main-require-bank-branch') ) {
+      $payinfo =~ /^(\d+)\@(\d+)\.(\d+)$/ or return 'invalid echeck account@branch.bank';
+      $payinfo = "$1\@$2.$3";
+    }
+    elsif ( $conf->exists('echeck-nonus') ) {
       $payinfo =~ /^(\d+)\@(\d+)$/ or return 'invalid echeck account@aba';
       $payinfo = "$1\@$2";
     } else {
@@ -1892,12 +1902,17 @@ sub check {
     $self->paycvv('');
 
     unless ( $ignore_banned_card ) {
-      my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+      my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } );
       if ( $ban ) {
-        return 'Banned ACH account: banned on '.
-               time2str('%a %h %o at %r', $ban->_date).
-               ' by '. $ban->otaker.
-               ' (ban# '. $ban->bannum. ')';
+        if ( $ban->bantype eq 'warn' ) {
+          #or others depending on value of $ban->reason ?
+          return '_duplicate_ach' unless $self->override_ban_warn;
+        } else {
+          return 'Banned ACH account: banned on '.
+                 time2str('%a %h %o at %r', $ban->_date).
+                 ' by '. $ban->otaker.
+                 ' (ban# '. $ban->bannum. ')';
+        }
       }
     }
 
@@ -2165,7 +2180,7 @@ sub cancel {
     return ( "Can't (yet) ban encrypted credit cards" )
       if $self->is_encrypted($self->payinfo);
 
-    my $ban = new FS::banned_pay $self->_banned_pay_hashref;
+    my $ban = new FS::banned_pay $self->_new_banned_pay_hashref;
     my $error = $ban->insert;
     return ( $error ) if $error;
 
@@ -2199,11 +2214,18 @@ sub _banned_pay_hashref {
 
   {
     'payby'   => $payby2ban{$self->payby},
-    'payinfo' => md5_base64($self->payinfo),
+    'payinfo' => $self->payinfo,
     #don't ever *search* on reason! #'reason'  =>
   };
 }
 
+sub _new_banned_pay_hashref {
+  my $self = shift;
+  my $hr = $self->_banned_pay_hashref;
+  $hr->{payinfo} = md5_base64($hr->{payinfo});
+  $hr;
+}
+
 =item notes
 
 Returns all notes (see L<FS::cust_main_note>) for this customer.
@@ -2211,12 +2233,13 @@ Returns all notes (see L<FS::cust_main_note>) for this customer.
 =cut
 
 sub notes {
-  my $self = shift;
-  #order by?
+   my($self,$orderby_classnum) = (shift,shift);
+   my $orderby = "_DATE DESC";
+   $orderby = "CLASSNUM ASC, $orderby" if $orderby_classnum;
   qsearch( 'cust_main_note',
            { 'custnum' => $self->custnum },
           '',
-          'ORDER BY _DATE DESC'
+          "ORDER BY $orderby",
         );
 }
 
@@ -3367,7 +3390,7 @@ sub charge {
 sub charge_postal_fee {
   my $self = shift;
 
-  my $pkgpart = $conf->config('postal_invoice-fee_pkgpart');
+  my $pkgpart = $conf->config('postal_invoice-fee_pkgpart', $self->agentnum);
   return '' unless ($pkgpart && grep { $_ eq 'POST' } $self->invoicing_list);
 
   my $cust_pkg = new FS::cust_pkg ( {
@@ -3810,6 +3833,9 @@ Returns a status string for this customer, currently:
 
 =back
 
+Behavior of inactive vs. cancelled edge cases can be adjusted with the
+cust_main-status_module configuration option.
+
 =cut
 
 sub status { shift->cust_status(@_); }
@@ -3847,21 +3873,11 @@ Returns a hex triplet color string for this customer's status.
 
 =cut
 
-use vars qw(%statuscolor);
-tie %statuscolor, 'Tie::IxHash',
-  'prospect'  => '7e0079', #'000000', #black?  naw, purple
-  'active'    => '00CC00', #green
-  'ordered'   => '009999', #teal? cyan?
-  'suspended' => 'FF9900', #yellow
-  'cancelled' => 'FF0000', #red
-  'inactive'  => '0000CC', #blue
-;
-
 sub statuscolor { shift->cust_statuscolor(@_); }
 
 sub cust_statuscolor {
   my $self = shift;
-  $statuscolor{$self->cust_status};
+  __PACKAGE__->statuscolors->{$self->cust_status};
 }
 
 =item tickets
@@ -3957,8 +3973,8 @@ Class method that returns the list of possible status strings for customers
 =cut
 
 sub statuses {
-  #my $self = shift; #could be class...
-  keys %statuscolor;
+  my $self = shift;
+  keys %{ $self->statuscolors };
 }
 
 =item cust_status_sql
@@ -4002,13 +4018,14 @@ sub prospect_sql {
 =item ordered_sql
 
 Returns an SQL expression identifying ordered cust_main records (customers with
-recurring packages not yet setup).
+no active packages, but recurring packages not yet setup or one time charges
+not yet billed).
 
 =cut
 
 sub ordered_sql {
   FS::cust_main->none_active_sql.
-  " AND 0 < ( $select_count_pkgs AND ". FS::cust_pkg->ordered_sql. " ) ";
+  " AND 0 < ( $select_count_pkgs AND ". FS::cust_pkg->not_yet_billed_sql. " ) ";
 }
 
 =item active_sql
@@ -4067,22 +4084,7 @@ Returns an SQL expression identifying cancelled cust_main records.
 
 =cut
 
-sub cancelled_sql { cancel_sql(@_); }
-sub cancel_sql {
-
-  my $recurring_sql = FS::cust_pkg->recurring_sql;
-  my $cancelled_sql = FS::cust_pkg->cancelled_sql;
-
-  "
-        0 < ( $select_count_pkgs )
-    AND 0 < ( $select_count_pkgs AND $recurring_sql AND $cancelled_sql   )
-    AND 0 = ( $select_count_pkgs AND $recurring_sql
-                  AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
-            )
-  ";
-#    AND 0 = (  $select_count_pkgs AND ". FS::cust_pkg->inactive_sql. " )
-
-}
+sub cancel_sql { shift->cancelled_sql(@_); }
 
 =item uncancel_sql
 =item uncancelled_sql