multiple payment options, RT#23741
authorIvan Kohler <ivan@freeside.biz>
Tue, 10 Feb 2015 09:38:56 +0000 (01:38 -0800)
committerIvan Kohler <ivan@freeside.biz>
Tue, 10 Feb 2015 09:38:56 +0000 (01:38 -0800)
33 files changed:
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/Template_Mixin.pm
FS/FS/Upgrade.pm
FS/FS/cust_main.pm
FS/FS/cust_main/Location.pm
FS/FS/cust_main/Search.pm
FS/FS/cust_payby.pm
FS/FS/o2m_Common.pm
FS/FS/part_event/Action/cust_bill_realtime_lec.pm [deleted file]
FS/FS/part_event/Condition/has_cust_payby_auto.pm [new file with mode: 0644]
FS/FS/part_event/Condition/hasnt_cust_payby_auto.pm [new file with mode: 0644]
FS/FS/part_event/Condition/payby.pm [deleted file]
FS/FS/part_event_condition.pm
FS/FS/payby.pm
httemplate/edit/cust_main.cgi
httemplate/edit/cust_main/billing.html
httemplate/edit/cust_main/bottomfixup.js
httemplate/edit/cust_main/contact.html [deleted file]
httemplate/edit/cust_main/contacts_new.html
httemplate/edit/cust_main/cust_payby.html [new file with mode: 0644]
httemplate/edit/elements/edit.html
httemplate/edit/process/cust_main.cgi
httemplate/elements/menu.html
httemplate/elements/tr-coords.html
httemplate/search/cust_main.html
httemplate/search/cust_payby.html [new file with mode: 0644]
httemplate/search/report_cust_main.html
httemplate/search/report_cust_payby.html [new file with mode: 0644]
httemplate/view/cust_main.cgi
httemplate/view/cust_main/billing.html
httemplate/view/cust_main/cust_payby.html [new file with mode: 0644]
httemplate/view/cust_main/packages.html

index 2b959e6..4497916 100644 (file)
@@ -2249,7 +2249,7 @@ and customer address. Include units.',
     'section'     => 'self-service',
     'description' => 'Acceptable payment types for the signup server',
     'type'        => 'selectmultiple',
-    'select_enum' => [ qw(CARD DCRD CHEK DCHK PREPAY PPAL BILL COMP) ],
+    'select_enum' => [ qw(CARD DCRD CHEK DCHK PREPAY PPAL ) ], # BILL COMP) ],
   },
 
   {
@@ -2652,13 +2652,13 @@ and customer address. Include units.',
     'section'     => 'billing',
     'description' => 'Available payment types.',
     'type'        => 'selectmultiple',
-    'select_enum' => [ qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD MCHK PPAL COMP) ],
+    'select_enum' => [ qw(CARD DCRD CHEK DCHK CASH WEST MCRD MCHK PPAL) ],
   },
 
   {
     'key'         => 'payby-default',
-    'section'     => 'UI',
-    'description' => 'Default payment type.  HIDE disables display of billing information and sets customers to BILL.',
+    'section'     => 'deprecated',
+    'description' => 'Deprecated; in 4.x there is no longer the concept of a single "payment type".  Used to indicate the default payment type.  HIDE disables display of billing information and sets customers to BILL.',
     'type'        => 'select',
     'select_enum' => [ '', qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD PPAL COMP HIDE) ],
   },
index 133b6d8..54a4680 100644 (file)
@@ -1651,6 +1651,9 @@ sub tables_hashref {
         'bill_locationnum', 'int', 'NULL', '', '', '',
         'ship_locationnum', 'int', 'NULL', '', '', '',
         'taxstatusnum',   'char', 'NULL',      32, '', '',
+        'complimentary', 'char', 'NULL', 1, '', '',
+        'po_number', 'varchar', 'NULL', $char_d, '', '',
+        'invoice_attn', 'varchar', 'NULL', $char_d, '', '',
       ],
       'primary_key'  => 'custnum',
       'unique'       => [ [ 'agentnum', 'agent_custid' ] ],
@@ -1699,7 +1702,7 @@ sub tables_hashref {
       'columns' => [
         'custpaybynum', 'serial',     '',        '', '', '', 
         'custnum',         'int',     '',        '', '', '',
-        'weight',          'int',     '',        '', '', '', 
+        'weight',          'int', 'NULL',        '', '', '', 
         'payby',          'char',     '',         4, '', '', 
         'payinfo',     'varchar', 'NULL',       512, '', '', 
         'cardtype',    'varchar', 'NULL',   $char_d, '', '',
index 95d001e..fe484a4 100644 (file)
@@ -316,9 +316,6 @@ sub print_generic {
     unless $format =~ /^(latex|html|template)$/;
 
   my $cust_main = $self->cust_main || $self->prospect_main;
-  $cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
-    unless $cust_main->payname
-        && $cust_main->payby !~ /^(CARD|DCRD|CHEK|DCHK)$/;
 
   my $locale = $params{'locale'} || $cust_main->locale;
 
@@ -565,9 +562,11 @@ sub print_generic {
     'custnum'         => $cust_main->display_custnum,
     'prospectnum'     => $cust_main->prospectnum,
     'agent_custid'    => &$escape_function($cust_main->agent_custid),
-    ( map { $_ => &$escape_function($cust_main->$_()) } qw(
-      payname company address1 address2 city state zip fax
-    )),
+    ( map { $_ => &$escape_function($cust_main->$_()) }
+        qw( company address1 address2 city state zip fax )
+    ),
+    'payname'         => &$escape_function( $cust_main->invoice_attn
+                                             || $cust_main->contact_firstlast ),
 
     #global config
     'ship_enable'     => $conf->exists('invoice-ship_address'),
@@ -655,10 +654,10 @@ sub print_generic {
   my @address = ();
   $invoice_data{'address'} = \@address;
   push @address,
-    $cust_main->payname.
-      ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo
-        ? " (P.O. #". $cust_main->payinfo. ")"
-        : ''
+    $invoice_data{'payname'}.
+      ( $cust_main->po_number
+          ? " (P.O. #". $cust_main->po_number. ")"
+          : ''
       )
   ;
   push @address, $cust_main->company
index d05b309..263be80 100644 (file)
@@ -309,7 +309,10 @@ sub upgrade_data {
 
   tie my %hash, 'Tie::IxHash', 
 
-    #cust_main (remove paycvv from history)
+    #payby conditions to new ones
+    'part_event_condition' => [],
+
+    #cust_main (remove paycvv from history, locations, cust_payby, etc)
     'cust_main' => [],
 
     #contact -> cust_contact / prospect_contact
index cd675f9..d38f3d0 100644 (file)
@@ -76,6 +76,7 @@ use FS::Locales;
 use FS::upgrade_journal;
 use FS::sales;
 use FS::cust_payby;
+use FS::contact;
 
 # 1 is mostly method/subroutine entry and options
 # 2 traces progress of some operations
@@ -95,8 +96,6 @@ our $ucfirst_nowarn = 0;
 our @encrypted_fields = ('payinfo', 'paycvv');
 sub nohistory_fields { ('payinfo', 'paycvv'); }
 
-our @paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings');
-
 our $conf;
 #ask FS::UID to run this stuff for us later
 #$FS::UID::callback{'FS::cust_main'} = sub { 
@@ -331,7 +330,7 @@ invoicing_list destination to the newly-created svc_acct.  Here's an example:
   $cust_main->insert( {}, [ $email, 'POST' ] );
 
 Currently available options are: I<depend_jobnum>, I<noexport>,
-I<tax_exemption> and I<prospectnum>.
+I<tax_exemption>, I<prospectnum>, I<contact> and I<contact_params>.
 
 If I<depend_jobnum> is set, all provisioning jobs will have a dependancy
 on the supplied jobnum (they will not run until the specific job completes).
@@ -351,6 +350,14 @@ If I<prospectnum> is set, moves contacts and locations from that prospect.
 If I<contact> is set to an arrayref of FS::contact objects, inserts those
 new contacts with this new customer.
 
+If I<contact_params> is set to a hashref of CGI parameters (and I<contact> is
+unset), inserts those new contacts with this new customer.  Handles CGI
+paramaters for an "m2" multiple entry field as passed by edit/cust_main.cgi
+
+If I<cust_payby_params> is set to a hashref o fCGI parameters, inserts those
+new stored payment records with this new customer.  Handles CGI parameters
+for an "m2" multiple entry field as passed by edit/cust_main.cgi
+
 =cut
 
 sub insert {
@@ -378,7 +385,7 @@ sub insert {
   my $payby = '';
   if ( $self->payby eq 'PREPAY' ) {
 
-    $self->payby('BILL');
+    $self->payby(''); #'BILL');
     $prepay_identifier = $self->payinfo;
     $self->payinfo('');
 
@@ -403,7 +410,7 @@ sub insert {
   } elsif ( $self->payby =~ /^(CASH|WEST|MCRD|MCHK|PPAL)$/ ) {
 
     $payby = $1;
-    $self->payby('BILL');
+    $self->payby(''); #'BILL');
     $amount = $self->paid;
 
   }
@@ -557,8 +564,10 @@ sub insert {
 
   }
 
-  my $contact = delete $options{'contact'};
-  if ( $contact ) {
+  warn "  setting contacts\n"
+    if $DEBUG > 1;
+
+  if ( my $contact = delete $options{'contact'} ) {
 
     foreach my $c ( @$contact ) {
       $c->custnum($self->custnum);
@@ -570,6 +579,34 @@ sub insert {
 
     }
 
+  } elsif ( my $contact_params = delete $options{'contact_params'} ) {
+
+    my $error = $self->process_o2m( 'table'  => 'contact',
+                                    'fields' => FS::contact->cgi_contact_fields,
+                                    'params' => $contact_params,
+                                  );
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  warn "  setting cust_payby\n"
+    if $DEBUG > 1;
+
+  if ( my $cust_payby_params = delete $options{'cust_payby_params'} ) {
+
+    my $error = $self->process_o2m(
+      'table'         => 'cust_payby',
+      'fields'        => FS::cust_payby->cgi_cust_payby_fields,
+      'params'        => $cust_payby_params,
+      'hash_callback' => \&FS::cust_payby::cgi_hash_callback,
+    );
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+
   }
 
   warn "  setting cust_main_exemption\n"
@@ -1122,10 +1159,9 @@ sub delete {
 
   #cust_tax_adjustment in financials?
   #cust_pay_pending?  ouch
-  #cust_recon?
   foreach my $table (qw(
     cust_main_invoice cust_main_exemption cust_tag cust_attachment contact
-    cust_location cust_main_note cust_tax_adjustment
+    cust_payby cust_location cust_main_note cust_tax_adjustment
     cust_pay_void cust_pay_batch queue cust_tax_exempt
   )) {
     foreach my $record ( qsearch( $table, { 'custnum' => $self->custnum } ) ) {
@@ -1250,13 +1286,10 @@ sub replace {
     if $DEBUG;
 
   my $curuser = $FS::CurrentUser::CurrentUser;
-  if (    $self->payby eq 'COMP'
-       && $self->payby ne $old->payby
-       && ! $curuser->access_right('Complimentary customer')
-     )
-  {
-    return "You are not permitted to create complimentary accounts.";
-  }
+  return "You are not permitted to create complimentary accounts."
+    if $self->complimentary eq 'Y'
+    && $self->complimentary ne $old->complimentary
+    && ! $curuser->access_right('Complimentary customer');
 
   local($ignore_expired_card) = 1
     if $old->payby  =~ /^(CARD|DCRD)$/
@@ -1285,8 +1318,8 @@ sub replace {
   my $dbh = dbh;
 
   for my $l (qw(bill_location ship_location)) {
-    my $old_loc = $old->$l;
-    my $new_loc = $self->$l;
+    #my $old_loc = $old->$l;
+    my $new_loc = $self->$l or next;
 
     # find the existing location if there is one
     $new_loc->set('custnum' => $self->custnum);
@@ -1403,21 +1436,19 @@ sub replace {
 
   }
 
-  if ( $self->payby =~ /^(CARD|CHEK|LECB)$/
-       && ( ( $self->get('payinfo') ne $old->get('payinfo')
-              && $self->get('payinfo') !~ /^99\d{14}$/ 
-            )
-            || grep { $self->get($_) ne $old->get($_) } qw(paydate payname)
-          )
-     )
-  {
+  if ( my $cust_payby_params = delete $options{'cust_payby_params'} ) {
 
-    # card/check/lec info has changed, want to retry realtime_ invoice events
-    my $error = $self->retry_realtime;
+    my $error = $self->process_o2m(
+      'table'         => 'cust_payby',
+      'fields'        => FS::cust_payby->cgi_cust_payby_fields,
+      'params'        => $cust_payby_params,
+      'hash_callback' => \&FS::cust_payby::cgi_hash_callback,
+    );
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error;
     }
+
   }
 
   unless ( $import || $skip_fuzzyfiles ) {
@@ -1555,6 +1586,8 @@ sub check {
     || $self->ut_flag('message_noemail')
     || $self->ut_enum('locale', [ '', FS::Locales->locales ])
     || $self->ut_currencyn('currency')
+    || $self->ut_alphan('po_number')
+    || $self->ut_enum('complimentary', [ '', 'Y' ])
   ;
 
   foreach (qw(company ship_company)) {
@@ -1809,6 +1842,11 @@ sub check {
 
   }
 
+  return "You are not permitted to create complimentary accounts."
+    if ! $self->custnum
+    && $self->complimentary eq 'Y'
+    && ! $FS::CurrentUser->CurrentUser->access_right('Complimentary customer');
+
   if ( $self->paydate eq '' || $self->paydate eq '-' ) {
     return "Expiration date required"
       # shouldn't payinfo_check do this?
@@ -1947,7 +1985,7 @@ sub cust_payby {
   qsearch({
     'table'    => 'cust_payby',
     'hashref'  => { 'custnum' => $self->custnum },
-    'order_by' => 'ORDER BY weight ASC',
+    'order_by' => "ORDER BY payby IN ('CARD','CHEK') DESC, weight ASC",
   });
 }
 
@@ -4816,18 +4854,31 @@ sub _upgrade_data { #class method
 
     while (my $cust_main = $search->fetch) {
 
-      my $cust_payby = new FS::cust_payby {
-        'custnum' => $cust_main->custnum,
-        'weight'  => 1,
-        map { $_ => $cust_main->$_(); } @payfields
-      };
+      unless ( $cust_main->payby =~ /^(BILL|COMP)$/ ) {
 
-      my $error = $cust_payby->insert;
-      die $error if $error;
+        my $cust_payby = new FS::cust_payby {
+          'custnum' => $cust_main->custnum,
+          'weight'  => 1,
+          map { $_ => $cust_main->$_(); } @payfields
+        };
+
+        my $error = $cust_payby->insert;
+        die $error if $error;
+
+      }
+
+      $cust_main->complimentary('Y') if $cust_main->payby eq 'COMP';
+
+      $cust_main->invoice_attn( $cust_main->payname )
+        if $cust_main->payby eq 'BILL' && $cust_main->payname;
+      $cust_main->po_number( $cust_main->payinfo )
+        if $cust_main->payby eq 'BILL' && $cust_main->payinfo;
 
       $cust_main->setfield($_, '') foreach @payfields;
-      $error = $cust_main->replace;
-      die $error if $error;
+      my $error = $cust_main->replace;
+      die "Error upgradging payment information for custnum ".
+          $cust_main->custnum. ": $error"
+        if $error;
 
     };
 
index be375dd..3cb73ff 100644 (file)
@@ -74,9 +74,11 @@ sub bill_location {
   $self->hashref->{bill_location} 
     ||= FS::cust_location->by_key($self->bill_locationnum)
     # degraded mode--let the system keep running during upgrades
-    ||  FS::cust_location->new({
-        map { $_ => $self->get($_) } @location_fields
-      })
+    ||  ( $self->get('address1')
+            && FS::cust_location->new({
+                 map { $_ => $self->get($_) } @location_fields
+               })
+        );
 }
 
 =item ship_location
@@ -89,9 +91,17 @@ sub ship_location {
   my $self = shift;
   $self->hashref->{ship_location}
     ||= FS::cust_location->by_key($self->ship_locationnum)
-    ||  FS::cust_location->new({
-        map { $_ => $self->get('ship_'.$_) || $self->get($_) } @location_fields
-      })
+    # degraded mode--let the system keep running during upgrades
+    ||  ( $self->get('ship_address1')
+            ? FS::cust_location->new({
+                map { $_ => $self->get('ship_'.$_) } @location_fields
+              })
+            : $self->get('address1')
+                ? FS::cust_location->new({
+                    map { $_ => $self->get($_) } @location_fields
+                  })
+                : ''
+        );
 
 }
 
index f0a7d41..097f2fb 100644 (file)
@@ -608,14 +608,6 @@ listref of start date, end date
 
 listref of start date, end date
 
-=item payby
-
-listref
-
-=item paydate_year
-
-=item paydate_month
-
 =item current_balance
 
 listref (list returned by FS::UI::Web::parse_lt_gt($cgi, 'current_balance'))
@@ -646,7 +638,6 @@ sub search {
     'status'        => '',
     'address'       => '',
     'zip'           => '',
-    'paydate_year'  => '',
     'invoice_terms' => '',
     'custbatch'     => '',
     %$params
@@ -911,40 +902,6 @@ sub search {
   }
 
   ###
-  # payby
-  ###
-
-  if ( $params->{'payby'} ) {
-
-    my @payby = ref( $params->{'payby'} )
-                  ? @{ $params->{'payby'} }
-                  :  ( $params->{'payby'} );
-
-    @payby = grep /^([A-Z]{4})$/, @payby;
-    my $in_payby = 'IN(' . join(',', map {"'$_'"} @payby) . ')';
-    push @where, "EXISTS( SELECT 1 FROM cust_payby WHERE payby $in_payby ".
-                 "AND cust_payby.custnum = cust_main.custnum)"
-      if @payby;
-  }
-
-  ###
-  # paydate_year / paydate_month
-  ###
-
-  if ( $params->{'paydate_year'} =~ /^(\d{4})$/ ) {
-    my $year = $1;
-    $params->{'paydate_month'} =~ /^(\d\d?)$/
-      or die "paydate_year without paydate_month?";
-    my $month = $1;
-
-    push @where,
-      'paydate IS NOT NULL',
-      "paydate != ''",
-      "CAST(paydate AS timestamp) < CAST('$year-$month-01' AS timestamp )"
-;
-  }
-
-  ###
   # invoice terms
   ###
 
@@ -1134,7 +1091,6 @@ sub search {
     'extra_headers' => \@extra_headers,
     'extra_fields'  => \@extra_fields,
   };
-  #warn Data::Dumper::Dumper($sql_query);
   $sql_query;
 
 }
index ad3d80a..a65a171 100644 (file)
@@ -1,26 +1,23 @@
 package FS::cust_payby;
+use base qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
 
 use strict;
-use base qw( FS::payinfo_Mixin FS::Record );
-use FS::UID;
-use FS::Record qw( qsearchs ); #qsearch;
-use FS::payby;
-use FS::cust_main;
 use Business::CreditCard qw( validate cardtype );
+use FS::UID qw( dbh );
 use FS::Msgcat qw( gettext );
+use FS::Record; #qw( qsearch qsearchs );
+use FS::payby;
+use FS::cust_main;
+use FS::banned_pay;
 
-use vars qw( $conf @encrypted_fields
-             $ignore_expired_card $ignore_banned_card
-             $ignore_invalid_card
-           );
-
-@encrypted_fields = ('payinfo', 'paycvv');
+our @encrypted_fields = ('payinfo', 'paycvv');
 sub nohistory_fields { ('payinfo', 'paycvv'); }
 
-$ignore_expired_card = 0;
-$ignore_banned_card = 0;
-$ignore_invalid_card = 0;
+our $ignore_expired_card = 0;
+our $ignore_banned_card = 0;
+our $ignore_invalid_card = 0;
 
+our $conf;
 install_callback FS::UID sub { 
   $conf = new FS::Conf;
   #yes, need it for stuff below (prolly should be cached)
@@ -141,15 +138,44 @@ otherwise returns false.
 
 =cut
 
-# the insert method can be inherited from FS::Record
+sub insert {
+  my $self = shift;
 
-=item delete
+  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;
+
+  my $error = $self->SUPER::insert;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
 
-Delete this record from the database.
+  if ( $self->payby =~ /^(CARD|CHEK)$/ ) {
+    # new auto card/check info, want to retry realtime_ invoice events
+    #  (new customer?  that's okay, they won't have any)
+    my $error = $self->cust_main->retry_realtime;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
 
-=cut
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
 
-# the delete method can be inherited from FS::Record
+}
+
+=item delete
+
+Delete this record from the database.
 
 =item replace OLD_RECORD
 
@@ -158,7 +184,87 @@ returns the error, otherwise returns false.
 
 =cut
 
-# the replace method can be inherited from FS::Record
+sub replace {
+  my $self = shift;
+
+  my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+              ? shift
+              : $self->replace_old;
+
+  if ( length($old->paycvv) && $self->paycvv =~ /^\s*[\*x]*\s*$/ ) {
+    $self->paycvv($old->paycvv);
+  }
+
+  if ( $self->payby =~ /^(CARD|DCRD)$/
+       && (    $self->payinfo =~ /xx/
+            || $self->payinfo =~ /^\s*N\/A\s+\(tokenized\)\s*$/
+          )
+     )
+  {
+warn $self->payinfo;
+warn $old->payinfo;
+    $self->payinfo($old->payinfo);
+
+  } elsif ( $self->payby =~ /^(CHEK|DCHK)$/ && $self->payinfo =~ /xx/ ) {
+    #fix for #3085 "edit of customer's routing code only surprisingly causes
+    #nothing to happen...
+    # this probably won't do the right thing when we don't have the
+    # public key (can't actually get the real $old->payinfo)
+    my($new_account, $new_aba) = split('@', $self->payinfo);
+    my($old_account, $old_aba) = split('@', $old->payinfo);
+    $new_account = $old_account if $new_account =~ /xx/;
+    $new_aba     = $old_aba     if $new_aba     =~ /xx/;
+    $self->payinfo($new_account.'@'.$new_aba);
+  }
+
+  local($ignore_expired_card) = 1
+    if $old->payby  =~ /^(CARD|DCRD)$/
+    && $self->payby =~ /^(CARD|DCRD)$/
+    && ( $old->payinfo eq $self->payinfo || $old->paymask eq $self->paymask );
+
+  local($ignore_banned_card) = 1
+    if (    $old->payby  =~ /^(CARD|DCRD)$/ && $self->payby =~ /^(CARD|DCRD)$/
+         || $old->payby  =~ /^(CHEK|DCHK)$/ && $self->payby =~ /^(CHEK|DCHK)$/ )
+    && ( $old->payinfo eq $self->payinfo || $old->paymask eq $self->paymask );
+
+  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;
+
+  my $error = $self->SUPER::replace($old);
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  if ( $self->payby =~ /^(CARD|CHEK)$/
+       && ( ( $self->get('payinfo') ne $old->get('payinfo')
+              && $self->get('payinfo') !~ /^99\d{14}$/ 
+            )
+            || grep { $self->get($_) ne $old->get($_) } qw(paydate payname)
+          )
+     )
+  {
+
+    # card/check/lec info has changed, want to retry realtime_ invoice events
+    my $error = $self->cust_main->retry_realtime;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
 
 =item check
 
@@ -174,7 +280,7 @@ sub check {
   my $error = 
     $self->ut_numbern('custpaybynum')
     || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
-    || $self->ut_number('weight')
+    || $self->ut_numbern('weight')
     #encrypted #|| $self->ut_textn('payinfo')
     #encrypted #|| $self->ut_textn('paycvv')
 #    || $self->ut_textn('paymask') #XXX something
@@ -207,7 +313,7 @@ sub check {
     my $payinfo = $self->payinfo;
     $payinfo =~ s/\D//g;
     $payinfo =~ /^(\d{13,16}|\d{8,9})$/
-      or return gettext('invalid_card'); # . ": ". $self->payinfo;
+      or return gettext('invalid_card'); #. ": ". $self->payinfo;
     $payinfo = $1;
     $self->payinfo($payinfo);
     validate($payinfo)
@@ -308,52 +414,23 @@ sub check {
       }
     }
 
-  } elsif ( $self->payby eq 'LECB' ) {
-
-    my $payinfo = $self->payinfo;
-    $payinfo =~ s/\D//g;
-    $payinfo =~ /^1?(\d{10})$/ or return 'invalid btn billing telephone number';
-    $payinfo = $1;
-    $self->payinfo($payinfo);
-    $self->paycvv('');
-
-  } elsif ( $self->payby eq 'BILL' ) {
-
-    $error = $self->ut_textn('payinfo');
-    return "Illegal P.O. number: ". $self->payinfo if $error;
-    $self->paycvv('');
-
-  } elsif ( $self->payby eq 'COMP' ) {
-
-    my $curuser = $FS::CurrentUser::CurrentUser;
-    if (    ! $self->custnum
-         && ! $curuser->access_right('Complimentary customer')
-       )
-    {
-      return "You are not permitted to create complimentary accounts."
-    }
-
-    $error = $self->ut_textn('payinfo');
-    return "Illegal comp account issuer: ". $self->payinfo if $error;
-    $self->paycvv('');
-
-  } elsif ( $self->payby eq 'PREPAY' ) {
-
-    my $payinfo = $self->payinfo;
-    $payinfo =~ s/\W//g; #anything else would just confuse things
-    $self->payinfo($payinfo);
-    $error = $self->ut_alpha('payinfo');
-    return "Illegal prepayment identifier: ". $self->payinfo if $error;
-    return "Unknown prepayment identifier"
-      unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
-    $self->paycvv('');
+#  } elsif ( $self->payby eq 'PREPAY' ) {
+#
+#    my $payinfo = $self->payinfo;
+#    $payinfo =~ s/\W//g; #anything else would just confuse things
+#    $self->payinfo($payinfo);
+#    $error = $self->ut_alpha('payinfo');
+#    return "Illegal prepayment identifier: ". $self->payinfo if $error;
+#    return "Unknown prepayment identifier"
+#      unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+#    $self->paycvv('');
 
   }
 
   if ( $self->paydate eq '' || $self->paydate eq '-' ) {
     return "Expiration date required"
       # shouldn't payinfo_check do this?
-      unless $self->payby =~ /^(BILL|PREPAY|CHEK|DCHK|LECB|CASH|WEST|MCRD|MCHK|PPAL)$/;
+      unless $self->payby =~ /^(CHEK|DCHK)$/;
     $self->paydate('');
   } else {
     my( $m, $y );
@@ -400,6 +477,247 @@ sub check {
   $self->SUPER::check;
 }
 
+sub _banned_pay_hashref {
+  my $self = shift;
+
+  my %payby2ban = (
+    'CARD' => 'CARD',
+    'DCRD' => 'CARD',
+    'CHEK' => 'CHEK',
+    'DCHK' => 'CHEK'
+  );
+
+  {
+    'payby'   => $payby2ban{$self->payby},
+    'payinfo' => $self->payinfo,
+    #don't ever *search* on reason! #'reason'  =>
+  };
+}
+
+=item paydate_mon_year
+
+Returns a two element list consisting of the paydate month and year.
+
+=cut
+
+sub paydate_mon_year {
+  my $self = shift;
+
+  my $date = $self->paydate; # || '12-2037';
+
+  #false laziness w/elements/select-month_year.html
+  if ( $date  =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
+    ( $2, $1 );
+  } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+    ( $1, $3 );
+  } else {
+    warn "unrecognized expiration date format: $date";
+    ( '', '' );
+  }
+
+}
+
+=item realtime_bop
+
+=cut
+
+sub realtime_bop {
+  my( $self, %opt ) = @_;
+
+  $opt{$_} = $self->$_() for qw( payinfo payname paydate );
+
+  if ( $self->locationnum ) {
+    my $cust_location = $self->cust_location;
+    $opt{$_} = $cust_location->$_() for qw( address1 address2 city state zip );
+  }
+
+  $self->cust_main->realtime_bop({
+    'method' => FS::payby->payby2bop( $self->payby ),
+    %opt,
+  });
+
+}
+
+=item paytypes
+
+Returns a list of valid values for the paytype field (bank account type for
+electronic check payment).
+
+=cut
+
+sub paytypes {
+  #my $class = shift;
+
+  ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings');
+}
+
+=item cgi_cust_payby_fields
+
+Returns the field names used in the web interface (including some pseudo-fields).
+
+=cut
+
+sub cgi_cust_payby_fields {
+  #my $class = shift;
+  [qw( payby payinfo paydate_month paydate_year paycvv payname weight
+       payinfo1 payinfo2 payinfo3 paytype paystate )];
+}
+
+=item cgi_hash_callback HASHREF
+
+Subroutine (not a class or object method).  Processes a hash reference
+of web interface contet (transfers the data from pseudo-fields to real fields).
+
+=cut
+
+sub cgi_hash_callback {
+  my $hashref = shift;
+
+  my %noauto = (
+    'CARD' => 'DCRD',
+    'CHEK' => 'DCHK',
+  );
+  $hashref->{payby} = $noauto{$hashref->{payby}}
+    if ! $hashref->{weight} && exists $noauto{$hashref->{payby}};
+
+  if ( $hashref->{payby} =~ /^(CHEK|DCHK)$/ ) {
+
+    unless ( grep $hashref->{$_}, qw( payinfo1 payinfo2 payinfo3 payname ) ) {
+      %$hashref = ();
+      return;
+    }
+
+    $hashref->{payinfo} = $hashref->{payinfo1}. '@';
+    $hashref->{payinfo} .= $hashref->{payinfo3}.'.' 
+      if $conf->config('echeck-country') eq 'CA';
+    $hashref->{payinfo} .= $hashref->{'payinfo2'};
+
+    $hashref->{payname} .= $hashref->{'payname_CHEK'};
+
+  } elsif ( $hashref->{payby} =~ /^(CARD|DCRD)$/ ) {
+
+    unless ( grep $hashref->{$_}, qw( payinfo paycvv payname ) ) {
+      %$hashref = ();
+      return;
+    }
+
+  }
+
+  $hashref->{paydate}= $hashref->{paydate_month}. '-'. $hashref->{paydate_year};
+
+}
+
+=item search_sql
+
+Class method.
+
+Returns a qsearch hash expression to search for parameters specified in HASHREF.
+Valid paramters are:
+
+=over 4
+
+=item payby
+
+listref
+
+=item paydate_year
+
+=item paydate_month
+
+
+=back
+
+=cut
+
+sub search_sql {
+  my ($class, $params) = @_;
+
+  my @where = ();
+  my $orderby;
+
+  # initialize these to prevent warnings
+  $params = {
+    'paydate_year'  => '',
+    %$params
+  };
+
+  ###
+  # payby
+  ###
+
+  if ( $params->{'payby'} ) {
+
+    my @payby = ref( $params->{'payby'} )
+                  ? @{ $params->{'payby'} }
+                  :  ( $params->{'payby'} );
+
+    @payby = grep /^([A-Z]{4})$/, @payby;
+    my $in_payby = 'IN(' . join(',', map {"'$_'"} @payby) . ')';
+    push @where, "cust_payby.payby $in_payby"
+      if @payby;
+  }
+
+  ###
+  # paydate_year / paydate_month
+  ###
+
+  if ( $params->{'paydate_year'} =~ /^(\d{4})$/ ) {
+    my $year = $1;
+    $params->{'paydate_month'} =~ /^(\d\d?)$/
+      or die "paydate_year without paydate_month?";
+    my $month = $1;
+
+    push @where,
+      'cust_payby.paydate IS NOT NULL',
+      "cust_payby.paydate != ''",
+      "CAST(cust_payby.paydate AS timestamp) < CAST('$year-$month-01' AS timestamp )"
+;
+  }
+  ##
+  # setup queries, subs, etc. for the search
+  ##
+
+  $orderby ||= 'ORDER BY custnum';
+
+  # here is the agent virtualization
+  push @where,
+    $FS::CurrentUser::CurrentUser->agentnums_sql(table => 'cust_main');
+
+  my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
+
+  my $addl_from = ' LEFT JOIN cust_main USING ( custnum ) ';
+  # always make address fields available in results
+  for my $pre ('bill_', 'ship_') {
+    $addl_from .= 
+      ' LEFT JOIN cust_location AS '.$pre.'location '.
+      'ON (cust_main.'.$pre.'locationnum = '.$pre.'location.locationnum) ';
+  }
+
+  my $count_query = "SELECT COUNT(*) FROM cust_payby $addl_from $extra_sql";
+
+  my @select = ( 'cust_payby.*',
+                 #'cust_main.custnum',
+                 # there's a good chance that we'll need these
+                 'cust_main.bill_locationnum',
+                 'cust_main.ship_locationnum',
+                 FS::UI::Web::cust_sql_fields($params->{'cust_fields'}),
+               );
+
+  my $select = join(', ', @select);
+
+  my $sql_query = {
+    'table'         => 'cust_payby',
+    'select'        => $select,
+    'addl_from'     => $addl_from,
+    'hashref'       => {},
+    'extra_sql'     => $extra_sql,
+    'order_by'      => $orderby,
+    'count_query'   => $count_query,
+  };
+  $sql_query;
+
+}
+
 =back
 
 =head1 BUGS
index d237bef..4f6d2e7 100644 (file)
@@ -103,6 +103,7 @@ sub process_o2m {
                  map { $_ => $opt{'params'}->{$add_param."_$_"} }
                      @{ $opt{'fields'} }
                );
+    &{ $opt{'hash_callback'} }( \%hash ) if $opt{'hash_callback'};
     #next unless grep { $_ =~ /\S/ } values %hash;
 
     my $new_obj = "FS::$table"->new( { %$hashref, %hash } );
@@ -117,6 +118,7 @@ sub process_o2m {
 
     my %hash = map { $_ => $opt{'params'}->{$add_param."_$_"} }
                @{ $opt{'fields'} };
+    &{ $opt{'hash_callback'} }( \%hash ) if $opt{'hash_callback'};
     next unless grep { $_ =~ /\S/ } values %hash;
 
     my $add_obj = "FS::$table"->new( { %$hashref, %hash } );
diff --git a/FS/FS/part_event/Action/cust_bill_realtime_lec.pm b/FS/FS/part_event/Action/cust_bill_realtime_lec.pm
deleted file mode 100644 (file)
index cd03ddc..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package FS::part_event::Action::cust_bill_realtime_lec;
-
-use strict;
-use base qw( FS::part_event::Action );
-
-sub description {
-  #'Run phone bill ("LEC") billing with a <a href="http://420.am/business-onlinepayment/">Business::OnlinePayment</a> realtime gateway';
-  'Run phone bill ("LEC") billing with a Business::OnlinePayment realtime gateway';
-}
-
-sub deprecated { 1; }
-
-sub eventtable_hashref {
-  { 'cust_bill' => 1 };
-}
-
-sub default_weight { 30; }
-
-sub do_action {
-  my( $self, $cust_bill ) = @_;
-
-  #my $cust_main = $self->cust_main($cust_bill);
-  my $cust_main = $cust_bill->cust_main;
-
-  $cust_bill->realtime_lec;
-}
-
-1;
diff --git a/FS/FS/part_event/Condition/has_cust_payby_auto.pm b/FS/FS/part_event/Condition/has_cust_payby_auto.pm
new file mode 100644 (file)
index 0000000..edfb7ec
--- /dev/null
@@ -0,0 +1,43 @@
+package FS::part_event::Condition::has_cust_payby_auto;
+
+use strict;
+use Tie::IxHash;
+use FS::payby;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+  'Customer has automatic payment information';
+}
+
+tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2shortname;
+delete $payby{'DCRD'};
+delete $payby{'DCHK'};
+
+sub option_fields {
+  (
+    'payby' => { 
+                 label         => 'Has automatic payment info',
+                 type          => 'select',
+                 options       => [ keys %payby ],
+                 option_labels => \%payby,
+               },
+  );
+}
+
+sub condition {
+  my( $self, $object ) = @_;
+
+  my $cust_main = $self->cust_main($object);
+
+  scalar( qsearch({ 
+    'table'     => 'cust_payby',
+    'hashref'   => { 'custnum' => $cust_main->custnum,
+                     'payby'   => $self->option('payby')
+                   },
+    'order_by'  => 'LIMIT 1',
+  }) );
+
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/hasnt_cust_payby_auto.pm b/FS/FS/part_event/Condition/hasnt_cust_payby_auto.pm
new file mode 100644 (file)
index 0000000..6655a63
--- /dev/null
@@ -0,0 +1,27 @@
+package FS::part_event::Condition::hasnt_cust_payby_auto;
+
+use strict;
+use Tie::IxHash;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+  'Customer does not have automatic payment information';
+}
+
+sub condition {
+  my( $self, $object ) = @_;
+
+  my $cust_main = $self->cust_main($object);
+
+  ! scalar( qsearch({ 
+    'table'     => 'cust_payby',
+    'hashref'   => { 'custnum' => $cust_main->custnum,
+                   },
+    'extra_sql' => "AND payby IN ( 'CARD', 'CHEK' )",
+    'order_by'  => 'LIMIT 1',
+  }) );
+
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/payby.pm b/FS/FS/part_event/Condition/payby.pm
deleted file mode 100644 (file)
index 16bf480..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package FS::part_event::Condition::payby;
-
-use strict;
-use Tie::IxHash;
-use FS::payby;
-
-use base qw( FS::part_event::Condition );
-
-sub description {
-  #'customer payment types: ';
-  'Customer payment type';
-}
-
-#something like this
-tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname;
-sub option_fields {
-  (
-    'payby' => { 
-                 label         => 'Customer payment type',
-                 #type          => 'select-multiple',
-                 type          => 'checkbox-multiple',
-                 options       => [ keys %payby ],
-                 option_labels => \%payby,
-               },
-  );
-}
-
-sub condition {
-  my( $self, $object ) = @_;
-
-  my $cust_main = $self->cust_main($object);
-
-  my $hashref = $self->option('payby') || {};
-  $hashref->{ $cust_main->payby };
-
-}
-
-sub condition_sql {
-  my( $self, $table ) = @_;
-
-  'cust_main.payby IN '. $self->condition_sql_option_option('payby');
-}
-
-1;
index ac2ee82..200049d 100644 (file)
@@ -354,6 +354,45 @@ sub order_conditions_sql {
 
 }
 
+sub _upgrade_data { #class method
+  my ($class, %opts) = @_;
+
+  foreach my $part_event_condition (
+    qsearch('part_event_condition', { 'conditionname' => 'payby' } )
+  ) {
+
+    my $payby = $part_event_condition->option('payby');
+
+    if ( scalar( keys %$payby ) == 1 ) {
+
+      if ( $payby->{'CARD'} ) {
+
+        $part_event_condition->conditionname('has_cust_payby_auto');
+
+      } elsif ( $payby->{'CHEK'} ) {
+
+        $part_event_condition->conditionname('has_cust_payby_auto');
+
+      }
+
+    } elsif ( $payby->{'BILL'} && ! $payby->{'CARD'} && ! $payby->{'CHEK'} ) {
+
+      $part_event_condition->conditionname('hasnt_cust_payby_auto');
+
+    } else {
+
+      die 'Unable to automatically convert payby condition for event #'.
+          $part_event_condition->eventpart. "\n";
+
+    }
+
+    my $error = $part_event_condition->replace;
+    die $error if $error;
+
+  }
+
+}
+
 =back
 
 =head1 BUGS
index c4aa1b1..13423c4 100644 (file)
@@ -5,7 +5,6 @@ use vars qw(%hash %payby2bop);
 use Tie::IxHash;
 use Business::CreditCard;
 
-
 =head1 NAME
 
 FS::payby - Object methods for payment type records
@@ -39,9 +38,8 @@ Payment types.
 =cut
 
 # paybys can be any/all of:
-# - a customer payment type (cust_main.payby)
+# - a customer saved payment type (cust_payby.payby)
 # - a payment or refund type (cust_pay.payby, cust_pay_batch.payby, cust_refund.payby)
-# - an event type (part_bill_event.payby)
 
 tie %hash, 'Tie::IxHash',
   'CARD' => {
@@ -70,18 +68,6 @@ tie %hash, 'Tie::IxHash',
     cust_pay  => 'CHEK', #this is a customer type only, payments are CHEK...
     realtime  => 1,
   },
-  #'LECB' => {
-  #  tinyname  => 'phone bill',
-  #  shortname => 'Phone bill billing',
-  #  longname  => 'Phone bill billing',
-  #  realtime  => 1,
-  #},
-  'BILL' => {
-    tinyname  => 'billing',
-    shortname => 'Billing',
-    payname   => 'Check',
-    longname  => 'Billing',
-  },
   'PPAL' => {
     tinyname  => 'PayPal',
     shortname => 'PayPal',
@@ -143,12 +129,6 @@ tie %hash, 'Tie::IxHash',
     longname  => 'Wire transfer',
     cust_main => '', #not a customer type
   },
-  'COMP' => {
-    tinyname  => 'comp',
-    shortname => 'Complimentary',
-    longname  => 'Complimentary',
-    cust_pay  => '', # (free) is depricated as a payment type in cust_pay
-  },
   'CBAK' => {
     tinyname  => 'chargeback',
     shortname => 'Chargeback',
@@ -234,6 +214,11 @@ sub cust_payby {
   grep { ! exists $hash{$_}->{cust_main} } $self->payby;
 }
 
+sub cust_payby2shortname {
+  my $self = shift;
+  map { $_ => $hash{$_}->{shortname} } $self->cust_payby;
+}
+
 sub cust_payby2longname {
   my $self = shift;
   map { $_ => $hash{$_}->{longname} } $self->cust_payby;
index da87bfc..f99cce2 100755 (executable)
@@ -1,8 +1,4 @@
-<& /elements/header.html,
-      $title,
-      '',
-      ' onUnload="myclose()"' #hmm, in billing.html
-&>
+<& /elements/header.html, $title, &>
 
 <& /elements/error.html &>
 
@@ -60,7 +56,7 @@
     </TABLE>
   </TD>
 </TR>
-<TR><TD STYLE="height:40px"></TD></TR>
+<TR><TD STYLE="height:14px"></TD></TR>
 <TR>
   <TD STYLE="width:650px">
     <FONT CLASS="fsinnerbox-title"><% mt('Service address') |h %></FONT>
@@ -105,16 +101,13 @@ function samechanged(what) {
 
 <BR>
 
-<& cust_main/contacts_new.html,
-             'cust_main' => $cust_main,
-&>
+<& cust_main/contacts_new.html, 'cust_main'=>$cust_main, &>
 
 %# billing info
 <& cust_main/billing.html, $cust_main,
                'payinfo'        => $payinfo,
                'invoicing_list' => \@invoicing_list,
 &>
-<BR>
 
 % my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly';
 % if (!$ro_comments || $cust_main->comments) {
@@ -156,18 +149,6 @@ function samechanged(what) {
 
 <INPUT TYPE="hidden" NAME="usernum" VALUE="<% $cust_main->usernum %>">
 
-%# cust_main/bottomfixup.js
-% foreach my $hidden (
-%    'payauto', 'billday',
-%    'payinfo', 'payinfo1', 'payinfo2', 'payinfo3', 'paytype',
-%    'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv',
-%    'paystart_month', 'paystart_year', 'payissue',
-%    'payip',
-%    'paid',
-% ) {
-    <INPUT TYPE="hidden" NAME="<% $hidden %>" VALUE="">
-% } 
-
 <& cust_main/bottomfixup.html, 'custnum' => $custnum &>
 
 <BR>
@@ -375,9 +356,6 @@ if ( $cgi->param('error') ) {
 
 }
 
-my %keep = map { $_=>1 } qw( error tagnum lock_agentnum lock_pkgpart );
-$cgi->delete( grep { !$keep{$_} && $_ !~ /^tax_/ } $cgi->param );
-
 my $title = $custnum ? 'Edit Customer' : 'Add Customer';
 $title = mt($title);
 $title .= ": ". $cust_main->name if $custnum;
index fcd8f01..fa392bb 100644 (file)
@@ -1,123 +1,7 @@
-%if ( $payby_default eq 'HIDE' ) {
-%  $cust_main->payby('BILL') unless $cust_main->payby;
-%  my $payby = $cust_main->payby;
-
-  <INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
-
-  <INPUT TYPE="hidden" NAME="<%$payby%>_payinfo" VALUE="<% $cust_main->paymask %>">
-
-% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate billday )) { 
-
-    <INPUT TYPE="hidden" NAME="<% $payby.'_'.$field %>" VALUE="<% $cust_main->get($field) %>">
-
-% } 
-
-%  #false laziness w/elements/select-month_year.html & view/cust_main/billing.html
-%  my( $mon, $year );
-%  my $date = $cust_main->paydate || '12-2037';
-%  if ( $date  =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
-%    ( $mon, $year ) = ( $2, $1 );
-%  } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
-%    ( $mon, $year ) = ( $1, $3 );
-%  } else {
-%    die "unrecognized expiration date format: $date";
-%  }
-
-  <INPUT TYPE="hidden" NAME="<%$payby%>_exp_month" VALUE="<% $mon %>">
-  <INPUT TYPE="hidden" NAME="<%$payby%>_exp_year"  VALUE="<% $year %>">
-
-  <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax %>">
-
-  <INPUT TYPE="hidden" NAME="invoicing_list" VALUE="<% join(', ', @invoicing_list) %>">
-
-% } else {
-%
 %  my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
 
-  <BR><FONT CLASS="fsinnerbox-title"><% mt('Billing information') |h %></FONT>
-  <% &ntable("#cccccc") %>
-
-    <TR>
-      <TD ALIGN="right" WIDTH="200"><%$r%><% mt('Billing type') |h %></TD>
-
   <SCRIPT>
 
-    var mywindow = -1;
-    function myopen(filename,windowname,properties) {
-      myclose();
-      mywindow = window.open(filename,windowname,properties);
-    }
-    function myclose() {
-      if ( mywindow != -1 )
-        mywindow.close();
-      mywindow = -1;
-    }
-
-    var achwindow = -1;
-    function achopen(filename,windowname,properties) {
-      achclose();
-      achwindow = window.open(filename,windowname,properties);
-    }
-    function achclose() {
-      if ( achwindow != -1 )
-        achwindow.close();
-      achwindow = -1;
-    }
-
-    function card_changed(what) {
-      if (
-             what.form.payinfo.value.substring(0, 4) == '4093' 
-          || what.form.payinfo.value.substring(0, 4) == '4911' 
-          || what.form.payinfo.value.substring(0, 4) == '4936' 
-          || what.form.payinfo.value.substring(0, 6) == '564132' 
-          || what.form.payinfo.value.substring(0, 2) == '63' 
-          || what.form.payinfo.value.substring(0, 2) == '67' 
-         )
-      {
-        what.form.paystart_month.disabled = false;
-        what.form.paystart_year.disabled = false;
-        what.form.payissue.disabled = false;
-        what.form.paystart_month.style.backgroundColor = '#ffffff';
-        what.form.paystart_year.style.backgroundColor = '#ffffff';
-        what.form.payissue.style.backgroundColor = '#ffffff';
-        document.getElementById('paystart_label').style.color = '#000000';
-        document.getElementById('payissue_label').style.color = '#000000';
-      } else {
-        what.form.paystart_month.disabled = true;
-        what.form.paystart_year.disabled = true;
-        what.form.payissue.disabled = true;
-        what.form.paystart_month.style.backgroundColor = '#dddddd';
-        what.form.paystart_year.style.backgroundColor = '#dddddd';
-        what.form.payissue.style.backgroundColor = '#dddddd';
-        document.getElementById('paystart_label').style.color = '#999999';
-        document.getElementById('payissue_label').style.color = '#999999';
-      }
-      return true;
-    }
-
-    function init_payauto_changed(){
-        var f = document.getElementById('CARD_payauto');
-        if(f != null) payauto_changed(f);
-        f = document.getElementById('CHEK_payauto');
-        if(f != null) payauto_changed(f);
-    }
-
-    function payauto_changed(payauto_field){
-        var select = (payauto_field.name == 'CARD_payauto') ? 'CARD_billday' : 'CHEK_billday';
-        var span = document.getElementById('td_'+select);
-        select = document.getElementById(select);
-        if (span == null || select == null) return;
-        if(payauto_field.checked) {
-            span.style.color = '#000000';
-            select.disabled = false;
-        }
-        else {
-            span.style.color = '#999999';
-            select.disabled = true;
-            //why? select.selectedIndex = 0;
-        }
-    }
-
     function tax_changed(what) {
       var num = document.getElementById(what.id + '_num'); 
       if ( what.checked ) {
     
   </SCRIPT>
 
-  <& /elements/init_overlib.html &>
-
-%  my $payby = $cust_main->payby;
-%  my $paytype = $cust_main->paytype;
-%  my( $account, $aba ) = split('@', $payinfo);
-%  my $branch = '';
-%  ($branch,$aba) = split('\.',$aba)
-%    if $conf->config('echeck-country') eq 'CA';
-%
-%  my $disabled = 'DISABLED style="background-color: #dddddd"';
-%  my $text_disabled = 'style="color: #999999"';
-%
-%  if ( $payby =~ /^(CARD|DCRD)$/ && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) {
-%    $disabled = 'style="background-color: #ffffff"';
-%    $text_disabled = 'style="color: #000000";'
-%  }
-%
-%  my $disable_payauto = $conf->exists('disable_payauto_default');
-%  my $CARD_payauto_checked =   $payby eq 'DCRD' ? ''
-%                             : $payby eq 'CARD' ? 'CHECKED'
-%                             : $disable_payauto ? '' : 'CHECKED';
-%  my $CHEK_payauto_checked =   $payby eq 'DCHK' ? ''
-%                             : $payby eq 'CHEK' ? 'CHECKED'
-%                             : $disable_payauto ? '' : 'CHECKED';
-%
-%  sub billday_options {
-%   my $curr_value = shift;
-%   my $ret = '';
-%   for my $billday ( 1 .. 28 ) {
-%       my $sel = '';
-%       $sel = "SELECTED='SELECTED'" if $curr_value == $billday;
-%       $ret .= "<OPTION VALUE='$billday' $sel>$billday</OPTION>";
-%   }
-%   $ret;
-%  }
-%
-%  my $card_billday_style = $payby eq 'CARD' ? '' : 'style="color: #999999"';
-%  my $chek_billday_style = $payby eq 'CHEK' ? '' : 'style="color: #999999"';
-%  my $card_billday_select_disabled = $payby eq 'CARD' ? '' : 'DISABLED';
-%  my $chek_billday_select_disabled = $payby eq 'CHEK' ? '' : 'DISABLED';
-%
-%  #false laziness w/view/cust_main/billing.html and misc/payment.cgi
-%  my $routing_label = $conf->config('echeck-country') eq 'US'
-%                        ? 'ABA/Routing number'
-%                        : 'Routing number';
-%  my $routing_size      = $conf->config('echeck-country') eq 'CA' ? 4 : 10;
-%  my $routing_maxlength = $conf->config('echeck-country') eq 'CA' ? 3 : 9;
-%
-%
-%  my %payby = (
-%
-%    'CARD' =>
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Card number').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $payinfo : '' ). qq!" MAXLENGTH=19 onChange="card_changed(this)" onKeyUp="card_changed(this)"></TD></TR>!.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Expiration').qq! </TD>!.
-%          '<TD WIDTH="408">'.
-%
-%          include('/elements/select-month_year.html',
-%                    'prefix' => 'CARD_exp',
-%                    'selected_date' =>
-%                      ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->paydate : '' ),
-%                 ).
-%
-%          '</TD></TR>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">!.emt('CVV2').qq!&nbsp;!.
-%
-%          qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">!.emt('help').qq!</A>)!.
-%          qq!</TD>!.
-%          '<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_paycvv" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ && !$cust_main->is_encrypted($cust_main->paycvv) ? $cust_main->paycvv : '' ). '" SIZE=4 MAXLENGTH=4>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200"><SPAN ID="paystart_label" $text_disabled>!.emt('Start date').qq! </SPAN></TD>!.
-%          '<TD WIDTH="408">'.
-%
-%          include('/elements/select-month_year.html',
-%                    'prefix' => 'CARD_paystart',
-%                    'disabled' => $disabled,
-%                    'empty_option' => 1,
-%                    'start_year' => 2000,
-%                    'end_year'   => (localtime())[5] + 1900,
-%                    'selected_date' => (
-%                      ( $payby =~ /^(CARD|DCRD)$/
-%                        && cardtype($payinfo) =~ /^(Switch|Solo)$/ )
-%                          ? $cust_main->paystart_month. '-'.
-%                            $cust_main->paystart_year 
-%                          : ''
-%                    )
-%                 ).
-%
-%        qq!<SPAN ID="payissue_label" $text_disabled>!.emt('or Issue number').qq! </SPAN>!.
-%          '<INPUT TYPE="text" NAME="CARD_payissue" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payissue : '' ). qq!" SIZE=3 MAXLENGTH=2 $disabled></TD></TR>!.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Exact name on card').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payname" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!.
-%
-%        qq!<TR><TD COLSPAN=2 WIDTH="608">!.
-%           qq!<INPUT TYPE="checkbox" onchange="payauto_changed(this);" ID="CARD_payauto" NAME="CARD_payauto" $CARD_payauto_checked> !.
-%           emt('Charge future payments to this [_1] automatically','credit card').'</TD></TR>'.
-%
-%      ( $conf->exists('cust_main-select-billday') ?
-%        qq!<TR><TD ALIGN="RIGHT" id="td_CARD_billday" $card_billday_style>
-%                    Charge on this day of each month</TD><TD> &nbsp; 
-%                   <SELECT id="CARD_billday" $card_billday_select_disabled NAME="CARD_billday">!
-%                . billday_options($cust_main->billday) . qq!</SELECT> </TD></TR>!
-%        : ''
-%      ).
-%
-%      '</TABLE>',
-%
-%    'CHEK' => 
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Account number').qq! </TD>!.
-%          qq!<TD><INPUT TYPE="text" SIZE=12 NAME="CHEK_payinfo1" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $account : '' ). '"></TD>'.
-%          qq!<TD ALIGN="right">!.emt('Type').qq!</TD><TD><SELECT NAME="CHEK_paytype">!.
-%            join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } @FS::cust_main::paytypes).
-%          qq!</SELECT></TD></TR>!.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt($routing_label).qq! </TD>!.
-%          qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" SIZE=$routing_size MAXLENGTH=$routing_maxlength NAME="CHEK_payinfo2" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $aba : '' ). qq!"> !.
-%          qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">!.emt('help').qq!</A>)!.
-%          qq!</TD></TR>!.
-%
-%        qq!<INPUT TYPE="hidden" NAME="CHEK_exp_month" VALUE="12">!.
-%        qq!<INPUT TYPE="hidden" NAME="CHEK_exp_year" VALUE="2037">!.
-%
-%        ( $conf->config('echeck-country') eq 'CA' ? 
-%               qq!<TR><TD ALIGN="right">$r !.emt('Branch number').qq!</TD><TD COLSPAN="3">
-%                   <INPUT TYPE="text" name="CHEK_payinfo3" VALUE="$branch" SIZE=6 MAXLENGTH=5></TD></TR>! : '' ).
-%   
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Bank name').qq! </TD>!.
-%          qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" NAME="CHEK_payname" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!.
-%      ( $conf->exists('show_bankstate') ?
-%          qq!<TR><TD ALIGN="right" WIDTH="200">$paystate_label</TD>!.
-%          qq!<TD COLSPAN="3" WIDTH="408">!.
-%          include('/elements/select-state.html',
-%                    'empty'   => emt('(choose)'),
-%                    'state'   => $cust_main->paystate,
-%                    'country' => $cust_main->country,
-%                    'prefix'  => 'CHEK_pay',
-%                 ). "</TD></TR>"
-%         : '<INPUT TYPE="hidden" NAME="CHEK_paystate" VALUE="'.
-%            $cust_main->paystate. '">'
-%       ).
-%
-%
-%        qq!<TR><TD COLSPAN=4 WIDTH="608">!.
-%           qq!<INPUT TYPE="checkbox" onchange="payauto_changed(this);" ID="CHEK_payauto" NAME="CHEK_payauto" $CHEK_payauto_checked> !.
-%           emt('Charge future payments to this [_1] automatically','electronic check').'</TD></TR>'.
-%
-%      ( $conf->exists('cust_main-select-billday') ?
-%        qq!<TR><TD ALIGN="RIGHT" id="td_CHEK_billday" $chek_billday_style>
-%                    Charge on this day of each month</TD><TD> &nbsp;
-%                   <SELECT id="CHEK_billday" $chek_billday_select_disabled NAME="CHEK_billday">!
-%                . billday_options($cust_main->billday) . qq!</SELECT> </TD></TR>!
-%        : ''
-%      ).
-%
-%      '</TABLE>',
-%
-%    'LECB' =>  
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Phone number').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="LECB_payinfo" VALUE="!. ( $payby eq 'LECB' ? $cust_main->payinfo : '' ). qq!" MAXLENGTH=15 SIZE=16></TD></TR>!.
-%
-%        qq!<INPUT TYPE="hidden" NAME="LECB_exp_month" VALUE="12">!.
-%        qq!<INPUT TYPE="hidden" NAME="LECB_exp_year" VALUE="2037">!.
-%        qq!<INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%    'BILL' =>  
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">!.emt('P.O.').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payinfo" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payinfo : '' ). qq!"></TD></TR>!.
-%
-%        qq!<INPUT TYPE="hidden" NAME="BILL_exp_month" VALUE="12">!.
-%        qq!<INPUT TYPE="hidden" NAME="BILL_exp_year" VALUE="2037">!.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">!.emt('Attention').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payname" VALUE="!. encode_entities( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%    'COMP' =>   
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Approved by').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""></TD></TR>!.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Expiration').qq! </TD>!.
-%          '<TD WIDTH="408">'.
-%
-%          include('/elements/select-month_year.html',
-%                    'prefix' => 'COMP_exp',
-%                    'selected_date' =>
-%                      ( $payby eq 'COMP' ? $cust_main->paydate : '' ),
-%                 ).
-%
-%          '</TD></TR>'.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%    'CASH' =>
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Amount').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CASH_paid" VALUE="!. ( $payby eq 'CASH' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%    'WEST' =>
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Amount').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="WEST_paid" VALUE="!. ( $payby eq 'WEST' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%    'MCRD' =>
-%
-%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
-%
-%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}!.emt('Amount').qq! </TD>!.
-%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="MCRD_paid" VALUE="!. ( $payby eq 'MCRD' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
-%
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%        '<TR><TD>&nbsp;</TD></TR>'.
-%
-%      '</TABLE>',
-%
-%  );
-%
-%  #this should use FS::payby
-%  my @allopt = qw( CARD CHEK LECB BILL CASH WEST MCRD COMP );
-%
-%  my %allopt = map { $_ => FS::payby->shortname($_) } @allopt;
-%
-%  if ( $cust_main->custnum ) {
-%    #don't offer CASH/WEST/MCRD initial payment types when editing customer
-%    delete $allopt{$_} for qw(CASH WEST MCRD);
-%  }
-%  
-%  my @options = grep exists( $allopt{$_} ), @payby;
-%
-%  my %payby2option = (
-%    ( map { $_ => $_ } @options ),
-%    'DCRD' => 'CARD',
-%    'DCHK' => 'CHEK',
-%  );
-
-  <TD WIDTH="408">
-    <& /elements/selectlayers.html,
-                  'field'      => 'payby',
-                  'curr_value' => $payby2option{$payby || $payby_default || $payby[0] },
-                  'options'    => \@options,
-                  'labels'     => \%allopt,
-                  'html_between' => '</TD></TR></TABLE>',
-                  'layer_callback' => sub { my $layer = shift; $payby{$layer}; },
-                  'onchange'    => 'init_payauto_changed();',
-    &>
+  <BR><FONT CLASS="fsinnerbox-title"><% mt('Billing information') |h %></FONT>
 
   <% &ntable("#cccccc") %>
 
-    <TR><TD>&nbsp;</TD></TR>
-
 %   my $curuser = $FS::CurrentUser::CurrentUser;
 %   my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups');
 %   if (    $conf->exists('cust_class-tax_exempt')
       </TD>
     </TR>
 
-    <TR>
-      <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_FAX" VALUE="FAX" <%
-
-        ( grep { $_ eq 'FAX' } @invoicing_list )
-          ? 'CHECKED'
-          : ''
-
-        %>> <% mt('Fax invoices') |h %> 
-
-      </TD>
-    </TR>
-
 % }
 
     <TR>
 % }
 
     <TR>
+      <TD ALIGN="right" WIDTH="200"><% mt('Charge card/e-check on this day of the month') |h %> </TD>
+      <TD>
+        <SELECT NAME="billday">
+          <% billday_options($cust_main->billday) %>
+        </SELECT>
+      </TD>
+    </TR>
+
+%  sub billday_options {
+%   my $curr_value = shift;
+%   my $ret = '';
+%   for my $billday ( 1 .. 28 ) {
+%       my $sel = '';
+%       $sel = "SELECTED='SELECTED'" if $curr_value == $billday;
+%       $ret .= "<OPTION VALUE='$billday' $sel>$billday</OPTION>";
+%   }
+%   $ret;
+%  }
+
+    <TR>
       <TD ALIGN="right" WIDTH="200"><% mt('Invoice terms') |h %> </TD>
       <TD WIDTH="408">
         <& /elements/select-terms.html,
@@ -673,27 +254,17 @@ function toggle(obj) {
 % }
 
   </TABLE>
+  <BR>
 
-  <% $r %><% mt('required fields') |h %> 
-% } 
-
-<script type="text/javascript">
-    init_payauto_changed();
-</script>
-
-<%once>
-
-my $paystate_label = FS::Msgcat::_gettext('paystate');
-$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/;
+  <FONT CLASS="fsinnerbox-title"><% mt('Payment information') |h %></FONT>
+  <& cust_payby.html, 'cust_main'=>$cust_main, &>  
 
-</%once>
 <%init>
 
 my( $cust_main, %options ) = @_;
 my @invoicing_list = @{ $options{'invoicing_list'} };
 my $payinfo = $options{'payinfo'};
 my $conf = new FS::Conf;
-my $payby_default = $conf->config('payby-default');
 
 my $money_char = $conf->config('money_char') || '$';
 
index 6a9deb9..97816aa 100644 (file)
@@ -5,7 +5,7 @@ my $conf = new FS::Conf;
 my $company_latitude  = $conf->config('company_latitude');
 my $company_longitude = $conf->config('company_longitude');
 
-my @fixups = ('copy_payby_fields', 'standardize_locations');
+my @fixups = ('standardize_locations');
 
 push @fixups, 'confirm_censustract_bill', 'confirm_censustract_ship'
     if $conf->exists('cust_main-require_censustract');
@@ -51,55 +51,12 @@ function do_submit() {
   document.CustomerForm.submit();
 }
 
-function copy_payby_fields() {
-  var layervars = new Array(
-    'payauto', 'billday',
-    'payinfo', 'payinfo1', 'payinfo2', 'payinfo3', 'paytype',
-    'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv',
-    'paystart_month', 'paystart_year', 'payissue',
-    'payip',
-    'paid'
-  );
-
-  var cf = document.CustomerForm;
-  var payby = cf.payby.options[cf.payby.selectedIndex].value;
-  for ( f=0; f < layervars.length; f++ ) {
-    var field = layervars[f];
-    copyelement( cf.elements[payby + '_' + field],
-                 cf.elements[field]
-               );
-  }
-  submit_continue();
-}
-
 <& /elements/standardize_locations.js,
   'callback' => 'submit_continue();',
   'billship' => 1,
   'with_census' => 1, # no with_firm, apparently
 &>
 
-function copyelement(from, to) {
-  if ( from == undefined ) {
-    to.value = '';
-  } else if ( from.type == 'select-one' ) {
-    to.value = from.options[from.selectedIndex].value;
-    //alert(from + " (" + from.type + "): " + to.name + " => (" + from.selectedIndex + ") " + to.value);
-  } else if ( from.type == 'checkbox' ) {
-    if ( from.checked ) {
-      to.value = from.value;
-    } else {
-      to.value = '';
-    }
-  } else {
-    if ( from.value == undefined ) {
-      to.value = '';
-    } else {
-      to.value = from.value;
-    }
-  }
-  //alert(from + " (" + from.type + "): " + to.name + " => " + to.value);
-}
-
 % # the value in pre+'censustract' is the confirmed censustract (either from
 % # the previous saved record, or from address standardization (if the backend
 % # supports it), or from an aborted previous submit. only need to reconfirm
diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html
deleted file mode 100644 (file)
index c2ebb09..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-<TABLE CLASS="fsinnerbox">
-
-<TR>
-  <TH ALIGN="right"><%$r%><% mt('Contact name (last, first)') |h %></TH>
-  <TD COLSPAN=7>
-    <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') |h %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> , 
-    <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') |h %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>>
-  </TD>
-% if ( $conf->exists('show_ss') && !$pre ) { 
-
-  <TD ALIGN="right"><% mt('SS#') |h %></TD>
-  <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $opt{ss} %>" SIZE=11></TD>
-% } elsif ( !$pre ) { 
-
-  <TD><INPUT TYPE="hidden" NAME="ss" VALUE="<% $opt{ss} %>"></TD>
-% } 
-</TR>
-
-% if ( $conf->exists('cust-email-high-visibility') && !$pre ) {
-    <TR>
-      <TD ALIGN="right" WIDTH="200">
-        <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum) 
-          ? $r : '' %>Email address(es)
-      </TD>
-      <TD bgcolor="#FFFF00">
-        <INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>">
-      </TD>
-    </TR>
-% }
-
-% unless ( $conf->exists('cust-edit-alt-field-order') ) { #standard order
-
-  <& company &>
-  <& location &>
-  <& phones &>
-  <& fax &>
-
-% } else { #alternate field order
-
-  <& phones &>
-  <& location &>
-  <& fax &>
-  <& company &>
-
-% }
-
-% if ( $conf->exists('show_stateid') && !$pre ) { 
-
-<TR>
-  <TD ALIGN="right"><% $stateid_label %></TD>
-  <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $opt{stateid} %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>></TD>
-  <TD ALIGN="right"><% $stateid_state_label %></TD>
-  <TD><& /elements/select-state.html,
-                   'state'    => $cust_main->stateid_state,
-                   'country'  => $cust_main->country,
-                   'prefix'   => 'stateid_',
-                   'onchange' => $onchange,
-                   'disabled' => $disabled,
-                   'style'    => \@style,
-      &>
-  </TD>
-</TR>
-% } elsif ( !$pre ) { 
-
-  <TD><INPUT TYPE="hidden" NAME="stateid" VALUE="<% $opt{stateid} %>"></TD>
-  <TD><INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $cust_main->stateid_state %>"></TD>
-% } 
-
-</TABLE>
-<%$r%><% mt('required fields') |h %><BR>
-
-<%def company>
-% my $display = ($cust_main->residential_commercial eq 'Commercial')
-%                 ? '' : 'none';
-  <TR ID="<%$pre%>company_row" STYLE="display:<%$display%>">
-    <TD ALIGN="right"><% mt('Company') |h %></TD>
-    <TD COLSPAN=7>
-      <INPUT TYPE="text" NAME="<%$pre%>company" ID="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') |h %>" SIZE=60 onChange="<% $onchange %>" <%$disabled%> <%$style%>>
-    </TD>
-  </TR>
-</%def>
-
-<%def location>
-  <& /elements/location.html,
-               'prefix'       => $pre,
-               'object'       => $cust_main,
-               'onchange'     => $onchange,
-               'disabled'     => $disabled,
-               'style'        => \@style,
-               'same_checked' => $opt{'same_checked'},
-               'geocode'      => $opt{'geocode'},
-               'censustract'  => $opt{'censustract'},
-  &>
-</%def>
-
-<%def phones>
-  <& /elements/tr-cust_main-phones.html,
-       'prefix'       => $pre,
-       'cust_main'    => $cust_main,
-       'onchange'     => $onchange,
-       'disabled'     => $disabled,
-       'style'        => $style,
-  &>
-</%def>
-
-<%def fax>
-  <TR>
-    <TD ALIGN="right"><% mt('Fax') |h %></TD>
-    <TD COLSPAN=5>
-      <INPUT TYPE="text" NAME="<%$pre%>fax" VALUE="<% $cust_main->get($pre.'fax') %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>>
-    </TD>
-  </TR>
-</%def>
-
-<%once>
-
-my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
-
-</%once>
-<%shared>
-
-my( %opt, $cust_main, $pre, $onchange, $disabled, @style, $style );
-
-</%shared>
-<%init>
-
-%opt = @_;
-$cust_main = $opt{'cust_main'};
-$pre       = $opt{'pre'};
-$onchange  = $opt{'onchange'};
-$disabled  = $opt{'disabled'};
-@style     = ( $opt{'style'} ? @{ $opt{'style'} } : () );
-
-$style = scalar(@style) ? 'STYLE="'. join(';', @style). '"' : '';
-
-my $conf = new FS::Conf;
-
-foreach (qw(ss stateid)) {
-  $opt{$_} = $cust_main->masked($_) unless exists $opt{$_};
-}
-
-#false laziness with ship state
-my $countrydefault = $conf->config('countrydefault') || 'US';
-$cust_main->set($pre.'country', $countrydefault )
-  unless $cust_main->get($pre.'country');
-
-my $statedefault = $conf->config('statedefault')
-                   || ($countrydefault eq 'US' ? 'CA' : '');
-$cust_main->set($pre.'state', $statedefault )
-  unless $cust_main->get($pre.'state')
-         || $cust_main->get($pre.'country') ne $countrydefault;
-
-$cust_main->set('stateid_state', $cust_main->state )
-  unless $pre || $cust_main->get('stateid_state');
-
-$opt{geocode} ||= $cust_main->get('geocode');
-
-$opt{censustract} ||= $cust_main->censustract;
-
-my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/
-                  ? 'Driver&rsquo;s License'
-                  : FS::Msgcat::_gettext('stateid') || 'Driver&rsquo;s License';
-my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/
-                        ? 'Driver&rsquo;s License State'
-                        : FS::Msgcat::_gettext('stateid_state') || 'Driver&rsquo;s License State';
-
-my @invoicing_list = $cust_main->invoicing_list;
-
-my $agentnum = $cust_main->agentnum if $cust_main->custnum;
-
-</%init>
index 0ab02b4..9ccb45f 100644 (file)
@@ -1,10 +1,9 @@
 <DIV ID="contacts_div" STYLE="display:<% $display %>">
-<BR>
 <FONT CLASS="fsinnerbox-title">Contacts</FONT>
 <% include('/edit/elements/edit.html',
      'embed'           => $opt{cust_main},
      'table'           => 'cust_main',
-     'labels'          => { 'contactnum'  => 'Contact',
+     'labels'          => { 'contactnum'  => '', #'Contact',
                             #'locationnum' => '&nbsp;',
                           },
      'fields'          => [
@@ -14,7 +13,7 @@
          'custnum'           => $opt{cust_main}->custnum,
          'm2m_method'        => 'cust_contact',
          'm2m_dstcol'        => 'contactnum',   
-         'm2_label'          => 'Contact',
+         'm2_label'          => ' ', #'Contact',
          'm2_error_callback' => $m2_error_callback,
        },
      ],
diff --git a/httemplate/edit/cust_main/cust_payby.html b/httemplate/edit/cust_main/cust_payby.html
new file mode 100644 (file)
index 0000000..cf0ada9
--- /dev/null
@@ -0,0 +1,56 @@
+<% include('/edit/elements/edit.html',
+     'embed'           => $opt{cust_main},
+     'tablenum'        => 1,
+     'table'           => 'cust_main',
+     'labels'          => { 'custpaybynum'  => '',
+                            #'locationnum' => '&nbsp;',
+                          },
+     'fields'          => [
+       { 'field'             => 'custpaybynum',
+         'type'              => 'cust_payby',
+         'colspan'           => 6,
+         #'custnum'           => $opt{cust_main}->custnum,
+         'm2m_method'        => 'cust_payby',
+         'm2m_dstcol'        => 'custpaybynum',   
+         'm2_label'          => ' ',
+         'm2_error_callback' => $m2_error_callback,
+       },
+     ],
+     'agent_virt'      => 1,
+    )
+%>
+</DIV>
+<%init>
+
+my %opt = @_;
+
+my $m2_error_callback = sub {
+  my($cgi, $object) = @_;
+
+  #process_o2m fields in process/cust_main-cust_payby.html
+  my $fields = FS::cust_payby->cgi_cust_payby_fields;
+  my @gfields = ( '', map "_$_", grep $_ !~ /^(payby|paydate_)/, @$fields );
+
+  map {
+        if ( /^custpaybynum(\d+)$/ ) {
+          my $num = $1;
+          if ( grep $cgi->param("custpaybynum$num$_"), @gfields ) {
+            my %hash = (
+              'custpaybynum' => scalar($cgi->param("custpaybynum$num")),
+              map { $_ => scalar($cgi->param("custpaybynum${num}_$_")) }
+                @$fields,
+            );
+            FS::cust_payby::cgi_hash_callback( \%hash );
+            FS::cust_payby->new( \%hash );
+          } else {
+            ();
+          }
+        } else {
+          ();
+        }
+      }
+      $cgi->param;
+};
+
+</%init>
+
index 4d5beee..5a7920b 100644 (file)
@@ -118,6 +118,8 @@ Example:
     # display, no <FORM>, no hidden fields for table name or primary key, no
     # display of primary key, no submit button, no html_foot, no footer)
     'embed' => $object, #need to pass the object
+    'tablenum' => 4, #need to specify a table number when using multiple
+                     #embedded edits on a page (and m2 stuff)
 
     #don't show the primary key label and value
     'no_pkey_display' => 1,
@@ -256,7 +258,7 @@ Example:
 
 % }
 
-% my $tablenum = 0;
+% my $tablenum = $opt{'tablenum'} || 0;
 <TABLE ID="TableNumber<% $tablenum++ %>" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
 
 % my $g_row = 0;
index 82ec50c..52a2608 100755 (executable)
@@ -29,29 +29,6 @@ $cgi->param('tax','') unless defined $cgi->param('tax');
 
 $cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] );
 
-my $payby = $cgi->param('payby');
-
-my %noauto = (
-  'CARD' => 'DCRD',
-  'CHEK' => 'DCHK',
-);
-$payby = $noauto{$payby}
-  if ! $cgi->param('payauto') && exists $noauto{$payby};
-
-$cgi->param('payby', $payby);
-
-if ( $payby ) {
-  if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
-      my $payinfo = $cgi->param('payinfo1'). '@';
-      $payinfo .= $cgi->param('payinfo3').'.' 
-            if $conf->config('echeck-country') eq 'CA';
-      $payinfo .= $cgi->param('payinfo2');
-      $cgi->param('payinfo',$payinfo);
-  }
-  $cgi->param('paydate',
-    $cgi->param( 'exp_month' ). '-'. $cgi->param( 'exp_year' ) );
-}
-
 my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
 push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
 push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
@@ -177,6 +154,9 @@ if ( $curuser->access_right('Edit customer tax exemptions') ) {
   };
 }
 
+$options{'contact_params'} = scalar($cgi->Vars);
+$options{'cust_payby_params'} = scalar($cgi->Vars);
+
 #perhaps this stuff should go to cust_main.pm
 if ( $new->custnum eq '' or $duplicate_of ) {
 
@@ -304,34 +284,12 @@ if ( $new->custnum eq '' or $duplicate_of ) {
   my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } ); 
   $error ||= "Old record not found!" unless $old;
 
-  if ( length($old->paycvv) && $new->paycvv =~ /^\s*\*+\s*$/ ) {
-    $new->paycvv($old->paycvv);
-  }
   if ($new->ss =~ /xx/) {
     $new->ss($old->ss);
   }
   if ($new->stateid =~ /^xxx/) {
     $new->stateid($old->stateid);
   }
-  if ( $new->payby =~ /^(CARD|DCRD)$/
-       && (    $new->payinfo =~ /xx/
-            || $new->payinfo =~ /^\s*N\/A\s+\(tokenized\)\s*$/
-          )
-     )
-  {
-    $new->payinfo($old->payinfo);
-
-  } elsif ( $new->payby =~ /^(CHEK|DCHK)$/ && $new->payinfo =~ /xx/ ) {
-    #fix for #3085 "edit of customer's routing code only surprisingly causes
-    #nothing to happen...
-    # this probably won't do the right thing when we don't have the
-    # public key (can't actually get the real $old->payinfo)
-    my($new_account, $new_aba) = split('@', $new->payinfo);
-    my($old_account, $old_aba) = split('@', $old->payinfo);
-    $new_account = $old_account if $new_account =~ /xx/;
-    $new_aba     = $old_aba     if $new_aba     =~ /xx/;
-    $new->payinfo($new_account.'@'.$new_aba);
-  }
 
   if ( ! $conf->exists('cust_main-edit_signupdate') or
        ! $new->signupdate ) {
@@ -353,13 +311,4 @@ if ( $new->custnum eq '' or $duplicate_of ) {
   
 }
 
-unless ( $error ) { #XXX i should be transactional... all in the insert
-                    # or replace call
-
-  $error = $new->process_o2m( 'table'  => 'contact',
-                              'fields' => FS::contact->cgi_contact_fields,
-                              'params' => scalar($cgi->Vars),
-                            );
-}
-
 </%init>
index 5f6921c..7d5d4f3 100644 (file)
@@ -127,6 +127,7 @@ $report_customers{'Advanced customer reports'} = [ $fsurl. 'search/report_cust_m
 if ( $curuser->access_right('List contacts') ) {
   $report_customers{'separator'} = '';
   $report_customers{'Customer contacts'} = [ $fsurl. 'search/report_contact.html?link=cust_main' ];
+  $report_customers{'Customer stored payment information'} = [ $fsurl. 'search/report_cust_payby.html' ];
 }
 
 tie my %report_invoices_open, 'Tie::IxHash',
index 3248dc2..5ac5ed4 100644 (file)
@@ -1,6 +1,6 @@
 <TR>
   <TD ALIGN="right"><% mt('Latitude') |h %></TD>
-  <TD COLSPAN=5>
+  <TD COLSPAN=7>
     <FONT STYLE="background-color: #ffffff; border: 1px solid #ffffff"><% $latitude %></FONT>
     &nbsp;<% mt('Longitude') |h %>
     <FONT STYLE="background-color: #ffffff; border: 1px solid #ffffff"><% $longitude %></FONT>
index 503e782..672c201 100755 (executable)
@@ -42,7 +42,7 @@ my %search_hash = ();
 #scalars
 my @scalars = qw (
   agentnum salesnum status address city county state zip country
-  paydate_year paydate_month invoice_terms
+  invoice_terms
   no_censustract with_geocode with_email tax no_tax POST no_POST
   custbatch usernum
   cancelled_pkgs
@@ -58,7 +58,7 @@ for my $param ( @scalars ) {
 }
 
 #lists
-for my $param (qw( classnum refnum payby tagnum pkg_classnum )) {
+for my $param (qw( classnum refnum tagnum pkg_classnum )) {
   $search_hash{$param} = [ $cgi->param($param) ];
 }
 
diff --git a/httemplate/search/cust_payby.html b/httemplate/search/cust_payby.html
new file mode 100644 (file)
index 0000000..140391b
--- /dev/null
@@ -0,0 +1,94 @@
+<& elements/search.html,
+                  'title'       => emt('Customer stored payment information results'),
+                  'menubar'     => $menubar,
+                  'name'        => emt('cards or bank accounts'), #??
+                  'query'       => $sql_query,
+                  'count_query' => $count_query,
+                  'header'      => [ @headers,
+                                     FS::UI::Web::cust_header(
+                                       $cgi->param('cust_fields')
+                                     ),
+                                   ],
+                  'fields'      => [
+                    @fields,
+                    \&FS::UI::Web::cust_fields,
+                  ],
+                  'color'       => [ 
+                                     ( map '', @fields ),
+                                     FS::UI::Web::cust_colors(),
+                                   ],
+                  'style'       => [
+                                     ( map '', @fields ),
+                                     FS::UI::Web::cust_styles(),
+                                   ],
+                  'align'       => [ 
+                                     ( map '', @fields ),
+                                     FS::UI::Web::cust_aligns(),
+                                   ],
+                  'links'       => [
+                                     ( map '', @fields ),
+                                     ( map { $_ ne 'Cust. Status' ? $link : '' }
+                                           FS::UI::Web::cust_header(
+                                                      $cgi->param('cust_fields')
+                                                                   )
+                                     ),
+                                   ],
+&>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Advanced customer search');
+
+my %search_hash = ();
+
+my @scalars = qw (
+  paydate_year paydate_month
+);
+
+for my $param ( @scalars ) {
+  $search_hash{$param} = scalar( $cgi->param($param) )
+    if length($cgi->param($param));
+}
+
+#lists
+for my $param (qw( payby )) {
+  $search_hash{$param} = [ $cgi->param($param) ];
+}
+
+###
+# etc
+###
+
+my $sql_query = FS::cust_payby->search_sql(\%search_hash);
+my $count_query   = delete($sql_query->{'count_query'});
+
+my @headers = ( 'Payment information',
+              );
+
+my @fields = ( sub { my $cust_payby = shift;
+                     FS::payby->shortname( $cust_payby->payby ). ' '.
+                       $cust_payby->paymask;
+                   }
+             );
+
+my $link = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+###
+# email links
+###
+
+my $menubar = [];
+
+#XXX TODO
+#if ( $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices') ) {
+#
+#  my $uri = new URI;
+#  $uri->query_form( \%search_hash );
+#  my $query = $uri->query;
+#
+#  push @$menubar, emt('Email a notice to these customers') =>
+#                    "${p}misc/email-customers.html?table=cust_main&$query",
+#
+#}
+
+</%init>
index cacb7de..ba7c99a 100755 (executable)
       <TH CLASS="background" COLSPAN=2 ALIGN="left"><FONT SIZE="+1"><% mt('Billing search options') |h %></FONT></TH>
     </TR>
 
-    <& /elements/tr-select-payby.html,
-                  'payby_type'   => 'cust',
-                  'multiple'     => 1,
-                  'all_selected' => 1,
-    &>
-
-    <TR>
-      <TD ALIGN="right"><% mt('Payment expiration before') |h %></TD>
-      <TD>
-        <SELECT NAME="paydate_month" DISABLED>
-%         foreach my $month ( 1 .. 12 ) {
-            <OPTION VALUE="<% $month %>"><% $month %></OPTION>
-%         }
-        </SELECT>
-        /
-        <SELECT NAME="paydate_year" onChange="paydate_year_changed(this);">
-          <OPTION VALUE=""></OPTION>
-%         my $lastyear = (localtime(time))[5] + 1899;
-%         foreach my $year ( $lastyear .. $lastyear+12 ) {
-            <OPTION VALUE="<% $year %>"><% $year %></OPTION>
-%         }
-        </SELECT>
-      </TD>
-    </TR>
-
-    <SCRIPT TYPE="text/javascript">
-      function paydate_year_changed(what) {
-        var value = what.options[what.selectedIndex].value;
-        var month_select = what.form.paydate_month;
-        if ( value == '' ) {
-          month_select.disabled = true;
-        } else {
-          month_select.disabled = false;
-        }
-      }
-    </SCRIPT>
-
 % my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups');
 % unless ( @exempt_groups ) { 
 
diff --git a/httemplate/search/report_cust_payby.html b/httemplate/search/report_cust_payby.html
new file mode 100644 (file)
index 0000000..81a8270
--- /dev/null
@@ -0,0 +1,57 @@
+<& /elements/header.html, mt('Customer stored payment infomation report') &>
+
+<FORM ACTION="cust_payby.html" METHOD="GET">
+
+  <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+    <& /elements/tr-select-payby.html,
+                  'payby_type'   => 'cust',
+                  'multiple'     => 1,
+                  'all_selected' => 1,
+    &>
+
+    <TR>
+      <TD ALIGN="right"><% mt('Payment expiration before') |h %></TD>
+      <TD>
+        <SELECT NAME="paydate_month" DISABLED>
+%         foreach my $month ( 1 .. 12 ) {
+            <OPTION VALUE="<% $month %>"><% $month %></OPTION>
+%         }
+        </SELECT>
+        /
+        <SELECT NAME="paydate_year" onChange="paydate_year_changed(this);">
+          <OPTION VALUE=""></OPTION>
+%         my $lastyear = (localtime(time))[5] + 1899;
+%         foreach my $year ( $lastyear .. $lastyear+12 ) {
+            <OPTION VALUE="<% $year %>"><% $year %></OPTION>
+%         }
+        </SELECT>
+      </TD>
+    </TR>
+
+    <SCRIPT TYPE="text/javascript">
+      function paydate_year_changed(what) {
+        var value = what.options[what.selectedIndex].value;
+        var month_select = what.form.paydate_month;
+        if ( value == '' ) {
+          month_select.disabled = true;
+        } else {
+          month_select.disabled = false;
+        }
+      }
+    </SCRIPT>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+
+</FORM>
+
+<& /elements/footer.html &>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Advanced customer search');
+
+</%init>
index d18c7f7..081b96b 100755 (executable)
@@ -169,14 +169,12 @@ function areyousure(href, message) {
 <TABLE BORDER=0>
 <TR>
   <TD VALIGN="top">
-    <& cust_main/contacts.html, $cust_main &>
+    <& cust_main/misc.html, $cust_main &>
+    <BR><& cust_main/contacts.html, $cust_main &>
   </TD>
   <TD VALIGN="top" STYLE="padding-left: 54px">
-    <& cust_main/misc.html, $cust_main &>
-% if ( $conf->config('payby-default') ne 'HIDE' ) { 
-      <BR><& cust_main/billing.html, $cust_main &>
-% } 
-
+    <& cust_main/billing.html, $cust_main &>
+    <BR><& cust_main/cust_payby.html, $cust_main &>
   </TD>
 </TR>
 <TR>
@@ -281,9 +279,7 @@ function areyousure(href, message) {
 
 % if ( $view eq 'payment_history' || $view eq 'jumbo' ) {
 
-% if ( $conf->config('payby-default') ne 'HIDE' ) { 
-  <& cust_main/payment_history.html, $cust_main &>
-% } 
+<& cust_main/payment_history.html, $cust_main &>
 
 % }
 
@@ -352,8 +348,7 @@ if ( $conf->config('ticket_system') ) {
 }
 $views{emt('Quotations')}      =  'quotations';
 $views{emt('Packages')}        =  'packages';
-$views{emt('Payment History')} =  'payment_history'
-                               unless $conf->config('payby-default' eq 'HIDE');
+$views{emt('Payment History')} =  'payment_history';
 $views{emt('Change History')}  =  'change_history'
   if $curuser->access_right('View customer history');
 $views{$conf->config('cust_main-custom_title') || emt('Custom')} =  'custom'
index 64ec591..f1125c0 100644 (file)
 % }
 
 % if ( $conf->exists('cust_main-select-billday') 
-%    && ($cust_main->payby eq 'CARD' || $cust_main->payby eq 'CHEK') ) {
-<TR>
-  <TD ALIGN="right"><% mt('Billing day of month') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->billday %>
-  </TD>
-</TR>
-% }
-
-<TR>
-  <TD ALIGN="right"><% mt('Billing type') |h %></TD>
-  <TD BGCOLOR="#ffffff">
-% if ( $cust_main->payby eq 'CARD' || $cust_main->payby eq 'DCRD' ) { 
-
-%   my $autodemand = $cust_main->payby eq 'CARD' ? 'automatic' : 'on-demand';
-    <% mt("Credit card ([_1])",$autodemand) |h %>
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Card number') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->paymask %></TD>
-</TR>
-%
-%#false laziness w/elements/select-month_year.html & edit/cust_main/billing.html
-%my( $mon, $year );
-%my $date = $cust_main->paydate || '12-2037';
-%if ( $date  =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
-%  ( $mon, $year ) = ( $2, $1 );
-%} elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
-%  ( $mon, $year ) = ( $1, $3 );
-%} else {
-%  warn "unrecognized expiration date format: $date";
-%  ( $mon, $year ) = ( '', '' );
-%}
-%
-
-<TR>
-  <TD ALIGN="right"><% mt('Expiration') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% "$mon/$year" %></TD>
-</TR>
-% if ( $cust_main->paystart_month ) { 
-
-  <TR>
-    <TD ALIGN="right"><% mt('Start date') |h %></TD>
-    <TD BGCOLOR="#ffffff"><% $cust_main->paystart_month. '/'. $cust_main->paystart_year %>
-  </TR>
-% } elsif ( $cust_main->payissue ) { 
-
-  <TR>
-    <TD ALIGN="right"><% mt('Issue #') |h %></TD>
-    <TD BGCOLOR="#ffffff"><% $cust_main->payissue %>
-  </TR>
-% } 
-
-
-<TR>
-  <TD ALIGN="right"><% mt('Name on card') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
-</TR>
-% } elsif ( $cust_main->payby eq 'CHEK' || $cust_main->payby eq 'DCHK') {
-%     my( $account, $aba ) = split('@', $cust_main->paymask );
-%  my $branch = '';
-%  ($branch,$aba) = split('\.',$aba) if $conf->config('echeck-country') eq 'CA';
-
-
-% my $autodemand = $cust_main->payby eq 'CHEK' ? 'automatic' : 'on-demand';
-    <% mt("Electronic check ([_1])",$autodemand) |h %>
-  </TD>
-</TR>
-
-%  #false laziness w/edit/cust_main/billing.html and misc/payment.cgi
-%  my $routing_label = $conf->config('echeck-country') eq 'US'
-%                        ? 'ABA/Routing number'
-%                        : 'Routing number';
-
-<TR>
-  <TD ALIGN="right"><% mt($routing_label) |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $aba %></TD>
-</TR>
-
-% if ( $conf->config('echeck-country') eq 'CA' ) {
-<TR>
-  <TD ALIGN="right"><% mt('Branch number') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $branch %></TD>
-<TR>
-% }
-
-  <TD ALIGN="right"><% mt('Account number') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $account %></TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Account type') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->paytype %></TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Bank name') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
-</TR>
-% if ( $conf->exists('show_bankstate') ) {
-<TR>
-  <TD ALIGN="right"><% $paystate_label %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->paystate || '&nbsp;&nbsp;&nbsp;' %></TD>
-</TR>
+%        && qsearch({ 'table'     => 'cust_payby',
+%                     'hashref'   => { 'custnum' => $cust_main->custnum, },
+%                     'extra_sql' => "AND payby IN ( 'CARD', 'CHEK' )",
+%                     'order_by'  => 'LIMIT 1',
+%                  })
+%    )
+% {
+    <TR>
+      <TD ALIGN="right"><% mt('Payment day of month') |h %></TD>
+      <TD BGCOLOR="#ffffff"><% $cust_main->billday %>
+      </TD>
+    </TR>
 % }
-% } elsif ( $cust_main->payby eq 'LECB' ) {
-%     $cust_main->payinfo =~ /^(\d{3})(\d{3})(\d{4})$/;
-%     my $payinfo = "$1-$2-$3";
-
-    <% mt('Phone bill billing') |h %> 
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Phone number') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $payinfo %></TD>
-</TR>
-% } elsif ( $cust_main->payby eq 'BILL' ) { 
-
-    <% mt('Billing') |h %> 
-  </TD>
-</TR>
-% if ( $cust_main->payinfo ) { 
-
-<TR>
-  <TD ALIGN="right"><% mt('P.O.') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->payinfo %></TD>
-</TR>
-% } 
 
-
-<TR>
-  <TD ALIGN="right"><% mt('Attention') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->payname |h %></TD>
-</TR>
-% } elsif ( $cust_main->payby eq 'COMP' ) { 
-
-    <% mt('Complimentary') |h %> 
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Authorized by') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% $cust_main->payinfo %></TD>
-</TR>
-%
-%#false laziness w/above etc.
-%my( $mon, $year );
-%my $date = $cust_main->paydate || '12-2037';
-%if ( $date  =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
-%  ( $mon, $year ) = ( $2, $1 );
-%} elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
-%  ( $mon, $year ) = ( $1, $3 );
-%} else {
-%  warn "unrecognized expiration date format: $date";
-%  ( $mon, $year ) = ( '', '' );
-%}
-%
-
-<TR>
-  <TD ALIGN="right"><% mt('Expiration') |h %></TD>
-  <TD BGCOLOR="#ffffff"><% "$mon/$year" %></TD>
-</TR>
+% if ( $cust_main->po_number ) { 
+    <TR>
+      <TD ALIGN="right"><% mt('Purchase Order #') |h %></TD>
+      <TD BGCOLOR="#ffffff"><% $cust_main->po_number %></TD>
+    </TR>
 % } 
 
 % my $yes = emt('yes');
 <TR>
   <TD ALIGN="right"><% mt('Postal mail invoices') |h %></TD>
   <TD BGCOLOR="#ffffff">
-    <% ( grep { $_ eq 'POST' } @invoicing_list ) ? $yes : $no %>
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Fax invoices') |h %></TD>
-  <TD BGCOLOR="#ffffff">
-    <% ( grep { $_ eq 'FAX' } @invoicing_list ) ? $yes : $no %>
+    <% ( grep { $_ eq 'POST' } @invoicing_list )
+         ? $yes. ( $cust_main->invoice_attn
+                     ? ', attn: '. $cust_main->invoice_attn
+                     : ''
+                 )
+         : $no
+    %>
   </TD>
 </TR>
 <TR>
 
 
 </TABLE>
-<%once>
-
-my $paystate_label = FS::Msgcat::_gettext('paystate');
-$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/;
-
-</%once>
 <%init>
 
 my( $cust_main ) = @_;
diff --git a/httemplate/view/cust_main/cust_payby.html b/httemplate/view/cust_main/cust_payby.html
new file mode 100644 (file)
index 0000000..3ebb055
--- /dev/null
@@ -0,0 +1,133 @@
+% if ( @cust_payby ) {
+
+    <FONT CLASS="fsinnerbox-title"><% mt('Payment information') |h %></FONT>
+    <TABLE CLASS="fsinnerbox">
+
+%   my $num = 0;
+%   foreach my $cust_payby ( @cust_payby ) {
+
+%     #one line per piece of info?  maybe, but just getting something working
+%     # for now
+
+%     if ( $cust_payby->payby eq 'CARD' || $cust_payby->payby eq 'DCRD' ) { 
+
+%       my $auto = $cust_payby->payby eq 'CARD' ? 'automatic' : 'on-demand';
+        <TR>
+          <TD COLSPAN=2 ALIGN="center" BGCOLOR="#ffffff">
+            <% mt("Credit card ([_1])",$auto) |h %>
+          </TD>
+        </TR>
+        <TR>
+          <TD ALIGN="right"><% mt('Card number') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $cust_payby->paymask %></TD>
+        </TR>
+
+%       my( $mon, $year ) = $cust_payby->paydate_mon_year;
+        <TR>
+          <TD ALIGN="right"><% mt('Expiration') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% "$mon/$year" %></TD>
+        </TR>
+
+%       if ( $cust_payby->paystart_month ) { 
+          <TR>
+            <TD ALIGN="right"><% mt('Start date') |h %></TD>
+            <TD BGCOLOR="#ffffff"><% $cust_payby->paystart_month. '/'. $cust_payby->paystart_year %>
+          </TR>
+%       } elsif ( $cust_payby->payissue ) { 
+          <TR>
+            <TD ALIGN="right"><% mt('Issue #') |h %></TD>
+            <TD BGCOLOR="#ffffff"><% $cust_payby->payissue %>
+          </TR>
+%       } 
+
+        <TR>
+          <TD ALIGN="right"><% mt('Name on card') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $cust_payby->payname %></TD>
+        </TR>
+
+%     } elsif ( $cust_payby->payby eq 'CHEK' || $cust_payby->payby eq 'DCHK') {
+
+%       my $auto = $cust_payby->payby eq 'CHEK' ? 'automatic' : 'on-demand';
+%
+%       my( $account, $aba ) = split('@', $cust_payby->paymask );
+%       my $branch = '';
+%       ($branch,$aba) = split('\.',$aba)
+%         if $conf->config('echeck-country') eq 'CA';
+
+        <TR>
+          <TD COLSPAN=2 ALIGN="center" BGCOLOR="#ffffff">
+            <% mt("Electronic check ([_1])",$auto) |h %>
+          </TD>
+        </TR>
+
+%       #false laziness w/edit/cust_main/billing.html and misc/payment.cgi
+%       my $routing_label = $conf->config('echeck-country') eq 'US'
+%                             ? 'ABA/Routing number'
+%                             : 'Routing number';
+        <TR>
+          <TD ALIGN="right"><% mt($routing_label) |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $aba %></TD>
+        </TR>
+
+%       if ( $conf->config('echeck-country') eq 'CA' ) {
+          <TR>
+            <TD ALIGN="right"><% mt('Branch number') |h %></TD>
+            <TD BGCOLOR="#ffffff"><% $branch %></TD>
+          </TR>
+%       }
+
+        <TR>
+          <TD ALIGN="right"><% mt('Account number') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $account %></TD>
+        </TR>
+        <TR>
+          <TD ALIGN="right"><% mt('Account type') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $cust_payby->paytype %></TD>
+        </TR>
+        <TR>
+          <TD ALIGN="right"><% mt('Bank name') |h %></TD>
+          <TD BGCOLOR="#ffffff"><% $cust_payby->payname %></TD>
+        </TR>
+
+%       if ( $conf->exists('show_bankstate') ) {
+          <TR>
+            <TD ALIGN="right"><% $paystate_label %></TD>
+            <TD BGCOLOR="#ffffff"><% $cust_payby->paystate || '&nbsp;&nbsp;&nbsp;' %></TD>
+          </TR>
+%       }
+
+%     } else {
+        <TR>
+          <TD COLSPAN="2"><FONT COLOR="#FF0000">
+            Unknown cust_pay.payby <% $cust_payby->payby %>
+          </FONT></TD>
+        </TR>
+%     }
+
+%     unless ( $num++ == $#cust_payby ) {
+        <TR>
+          <TD COLSPAN="2"></TD>
+        </TR>
+        <TR>
+          <TD COLSPAN="2" STYLE="border-top: 1px solid black; padding:2px"></TD>
+        </TR>
+%     }
+
+%   }
+
+    </TABLE>
+
+% }
+<%once>
+
+my $paystate_label = FS::Msgcat::_gettext('paystate');
+$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/;
+
+</%once>
+<%init>
+
+my( $cust_main ) = @_;
+my $conf = new FS::Conf;
+my @cust_payby = $cust_main->cust_payby;
+
+</%init>
index 9a2332b..9098511 100755 (executable)
@@ -92,9 +92,7 @@ if ( el ) el.scrollIntoView(true);
   <& /elements/order_pkg_link.html, 'cust_main'=>$cust_main &>
 % } 
 
-% if ( $curuser->access_right('One-time charge')
-%        && $conf->config('payby-default') ne 'HIDE'
-%      ) {
+% if ( $curuser->access_right('One-time charge') ) {
   <% $s++ ? ' | ' : '' %>
   <& /elements/one_time_charge_link.html, 'custnum'=>$cust_main->custnum &>
 % }