From a4d4d3df88b33a6db30b565921f6d62efb252351 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Tue, 10 Feb 2015 01:38:56 -0800 Subject: [PATCH] multiple payment options, RT#23741 --- FS/FS/Conf.pm | 8 +- FS/FS/Schema.pm | 5 +- FS/FS/Template_Mixin.pm | 19 +- FS/FS/Upgrade.pm | 5 +- FS/FS/cust_main.pm | 127 ++++-- FS/FS/cust_main/Location.pm | 22 +- FS/FS/cust_main/Search.pm | 44 -- FS/FS/cust_payby.pm | 442 ++++++++++++++++--- FS/FS/o2m_Common.pm | 2 + FS/FS/part_event/Action/cust_bill_realtime_lec.pm | 28 -- FS/FS/part_event/Condition/has_cust_payby_auto.pm | 43 ++ .../part_event/Condition/hasnt_cust_payby_auto.pm | 27 ++ FS/FS/part_event/Condition/payby.pm | 44 -- FS/FS/part_event_condition.pm | 39 ++ FS/FS/payby.pm | 27 +- httemplate/edit/cust_main.cgi | 28 +- httemplate/edit/cust_main/billing.html | 477 ++------------------- httemplate/edit/cust_main/bottomfixup.js | 45 +- httemplate/edit/cust_main/contact.html | 171 -------- httemplate/edit/cust_main/contacts_new.html | 5 +- httemplate/edit/cust_main/cust_payby.html | 56 +++ httemplate/edit/elements/edit.html | 4 +- httemplate/edit/process/cust_main.cgi | 57 +-- httemplate/elements/menu.html | 1 + httemplate/elements/tr-coords.html | 2 +- httemplate/search/cust_main.html | 4 +- httemplate/search/cust_payby.html | 94 ++++ httemplate/search/report_cust_main.html | 37 -- httemplate/search/report_cust_payby.html | 57 +++ httemplate/view/cust_main.cgi | 17 +- httemplate/view/cust_main/billing.html | 194 ++------- httemplate/view/cust_main/cust_payby.html | 133 ++++++ httemplate/view/cust_main/packages.html | 4 +- 33 files changed, 1034 insertions(+), 1234 deletions(-) delete mode 100644 FS/FS/part_event/Action/cust_bill_realtime_lec.pm create mode 100644 FS/FS/part_event/Condition/has_cust_payby_auto.pm create mode 100644 FS/FS/part_event/Condition/hasnt_cust_payby_auto.pm delete mode 100644 FS/FS/part_event/Condition/payby.pm delete mode 100644 httemplate/edit/cust_main/contact.html create mode 100644 httemplate/edit/cust_main/cust_payby.html create mode 100644 httemplate/search/cust_payby.html create mode 100644 httemplate/search/report_cust_payby.html create mode 100644 httemplate/view/cust_main/cust_payby.html diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 2b959e661..449791680 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -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) ], }, diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 133b6d81a..54a468067 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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, '', '', diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm index 95d001e83..fe484a462 100644 --- a/FS/FS/Template_Mixin.pm +++ b/FS/FS/Template_Mixin.pm @@ -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 diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm index d05b309c7..263be80b1 100644 --- a/FS/FS/Upgrade.pm +++ b/FS/FS/Upgrade.pm @@ -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 diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index cd675f9d4..d38f3d01b 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -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, I, -I and I. +I, I, I and I. If I 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 is set, moves contacts and locations from that prospect. If I is set to an arrayref of FS::contact objects, inserts those new contacts with this new customer. +If I is set to a hashref of CGI parameters (and I 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 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; }; diff --git a/FS/FS/cust_main/Location.pm b/FS/FS/cust_main/Location.pm index be375dda9..3cb73ffe8 100644 --- a/FS/FS/cust_main/Location.pm +++ b/FS/FS/cust_main/Location.pm @@ -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 + }) + : '' + ); } diff --git a/FS/FS/cust_main/Search.pm b/FS/FS/cust_main/Search.pm index f0a7d4137..097f2fb47 100644 --- a/FS/FS/cust_main/Search.pm +++ b/FS/FS/cust_main/Search.pm @@ -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; } diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm index ad3d80a20..a65a17133 100644 --- a/FS/FS/cust_payby.pm +++ b/FS/FS/cust_payby.pm @@ -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 diff --git a/FS/FS/o2m_Common.pm b/FS/FS/o2m_Common.pm index d237befa6..4f6d2e781 100644 --- a/FS/FS/o2m_Common.pm +++ b/FS/FS/o2m_Common.pm @@ -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 index cd03ddc3b..000000000 --- a/FS/FS/part_event/Action/cust_bill_realtime_lec.pm +++ /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 Business::OnlinePayment 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 index 000000000..edfb7eccb --- /dev/null +++ b/FS/FS/part_event/Condition/has_cust_payby_auto.pm @@ -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 index 000000000..6655a63d0 --- /dev/null +++ b/FS/FS/part_event/Condition/hasnt_cust_payby_auto.pm @@ -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 index 16bf48003..000000000 --- a/FS/FS/part_event/Condition/payby.pm +++ /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; diff --git a/FS/FS/part_event_condition.pm b/FS/FS/part_event_condition.pm index ac2ee822c..200049da7 100644 --- a/FS/FS/part_event_condition.pm +++ b/FS/FS/part_event_condition.pm @@ -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 diff --git a/FS/FS/payby.pm b/FS/FS/payby.pm index c4aa1b1b2..13423c48f 100644 --- a/FS/FS/payby.pm +++ b/FS/FS/payby.pm @@ -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; diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index da87bfca7..f99cce2de 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -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 @@ - + <% mt('Service address') |h %> @@ -105,16 +101,13 @@ function samechanged(what) {
-<& 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, &> -
% my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly'; % if (!$ro_comments || $cust_main->comments) { @@ -156,18 +149,6 @@ function samechanged(what) { -%# 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', -% ) { - -% } - <& cust_main/bottomfixup.html, 'custnum' => $custnum &>
@@ -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; diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html index fcd8f017c..fa392bbb7 100644 --- a/httemplate/edit/cust_main/billing.html +++ b/httemplate/edit/cust_main/billing.html @@ -1,123 +1,7 @@ -%if ( $payby_default eq 'HIDE' ) { -% $cust_main->payby('BILL') unless $cust_main->payby; -% my $payby = $cust_main->payby; - - - - - -% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate billday )) { - - - -% } - -% #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"; -% } - - - - - - - - -% } else { -% % my $r = qq!* !; -
<% mt('Billing information') |h %> - <% &ntable("#cccccc") %> - - - <%$r%><% mt('Billing type') |h %> - - <& /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 .= ""; -% } -% $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' => -% -% ''. -% -% qq!!. -% qq!!. -% -% qq!!. -% ''. -% -% qq!!. -% '!. -% '!. -% -% qq!!. -% qq!!. -% -% qq!'. -% -% ( $conf->exists('cust_main-select-billday') ? -% qq!! -% : '' -% ). -% -% '
${r}!.emt('Card number').qq!
${r}!.emt('Expiration').qq! '. -% -% include('/elements/select-month_year.html', -% 'prefix' => 'CARD_exp', -% 'selected_date' => -% ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->paydate : '' ), -% ). -% -% '
!.emt('CVV2').qq! !. -% -% qq!(!.emt('help').qq!)!. -% qq!'. -% -% qq!
!.emt('Start date').qq! '. -% -% 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!!.emt('or Issue number').qq! !. -% '
${r}!.emt('Exact name on card').qq!
!. -% qq! !. -% emt('Charge future payments to this [_1] automatically','credit card').'
-% Charge on this day of each month   -%
', -% -% 'CHEK' => -% -% ''. -% -% qq!!. -% qq!'. -% qq!!. -% -% qq!!. -% qq!!. -% -% qq!!. -% qq!!. -% -% ( $conf->config('echeck-country') eq 'CA' ? -% qq!! : '' ). -% -% qq!!. -% qq!!. -% ( $conf->exists('show_bankstate') ? -% qq!!. -% qq!" -% : '' -% ). -% -% -% qq!'. -% -% ( $conf->exists('cust_main-select-billday') ? -% qq!! -% : '' -% ). -% -% '
${r}!.emt('Account number').qq! !.emt('Type').qq!
${r}!.emt($routing_label).qq! !. -% qq!(!.emt('help').qq!)!. -% qq!
$r !.emt('Branch number').qq! -%
${r}!.emt('Bank name').qq!
$paystate_label!. -% include('/elements/select-state.html', -% 'empty' => emt('(choose)'), -% 'state' => $cust_main->paystate, -% 'country' => $cust_main->country, -% 'prefix' => 'CHEK_pay', -% ). "
!. -% qq! !. -% emt('Charge future payments to this [_1] automatically','electronic check').'
-% Charge on this day of each month   -%
', -% -% 'LECB' => -% -% ''. -% -% qq!!. -% qq!!. -% -% qq!!. -% qq!!. -% qq!!. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
${r}!.emt('Phone number').qq!
 
 
 
 
 
 
', -% -% 'BILL' => -% -% ''. -% -% qq!!. -% qq!!. -% -% qq!!. -% qq!!. -% -% qq!!. -% qq!!. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
!.emt('P.O.').qq!
!.emt('Attention').qq!
 
 
 
 
 
', -% -% 'COMP' => -% -% ''. -% -% qq!!. -% qq!!. -% -% qq!!. -% ''. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
${r}!.emt('Approved by').qq!
${r}!.emt('Expiration').qq! '. -% -% include('/elements/select-month_year.html', -% 'prefix' => 'COMP_exp', -% 'selected_date' => -% ( $payby eq 'COMP' ? $cust_main->paydate : '' ), -% ). -% -% '
 
 
 
 
 
', -% -% 'CASH' => -% -% ''. -% -% qq!!. -% qq!!. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
${r}!.emt('Amount').qq!
 
 
 
 
 
 
', -% -% 'WEST' => -% -% ''. -% -% qq!!. -% qq!!. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
${r}!.emt('Amount').qq!
 
 
 
 
 
 
', -% -% 'MCRD' => -% -% ''. -% -% qq!!. -% qq!!. -% -% ''. -% ''. -% ''. -% ''. -% ''. -% ''. -% -% '
${r}!.emt('Amount').qq!
 
 
 
 
 
 
', -% -% ); -% -% #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', -% ); - - - <& /elements/selectlayers.html, - 'field' => 'payby', - 'curr_value' => $payby2option{$payby || $payby_default || $payby[0] }, - 'options' => \@options, - 'labels' => \%allopt, - 'html_between' => '', - 'layer_callback' => sub { my $layer = shift; $payby{$layer}; }, - 'onchange' => 'init_payauto_changed();', - &> +
<% mt('Billing information') |h %> <% &ntable("#cccccc") %> -   - % my $curuser = $FS::CurrentUser::CurrentUser; % my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); % if ( $conf->exists('cust_class-tax_exempt') @@ -490,18 +63,6 @@ - - > <% mt('Fax invoices') |h %> - - - - % } @@ -544,6 +105,26 @@ % } + <% mt('Charge card/e-check on this day of the month') |h %> + + + + + +% sub billday_options { +% my $curr_value = shift; +% my $ret = ''; +% for my $billday ( 1 .. 28 ) { +% my $sel = ''; +% $sel = "SELECTED='SELECTED'" if $curr_value == $billday; +% $ret .= ""; +% } +% $ret; +% } + + <% mt('Invoice terms') |h %> <& /elements/select-terms.html, @@ -673,27 +254,17 @@ function toggle(obj) { % } +
- <% $r %><% mt('required fields') |h %> -% } - - - -<%once> - -my $paystate_label = FS::Msgcat::_gettext('paystate'); -$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/; + <% mt('Payment information') |h %> + <& cust_payby.html, 'cust_main'=>$cust_main, &> - <%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') || '$'; diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js index 6a9deb92a..97816aad7 100644 --- a/httemplate/edit/cust_main/bottomfixup.js +++ b/httemplate/edit/cust_main/bottomfixup.js @@ -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 index c2ebb093c..000000000 --- a/httemplate/edit/cust_main/contact.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - -% if ( $conf->exists('show_ss') && !$pre ) { - - - -% } elsif ( !$pre ) { - - -% } - - -% if ( $conf->exists('cust-email-high-visibility') && !$pre ) { - - - - -% } - -% 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 ) { - - - - - - - -% } elsif ( !$pre ) { - - - -% } - -
<%$r%><% mt('Contact name (last, first)') |h %> - <%$style%>> , - <%$style%>> - <% mt('SS#') |h %>
- <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum) - ? $r : '' %>Email address(es) - - -
<% $stateid_label %> <%$style%>><% $stateid_state_label %><& /elements/select-state.html, - 'state' => $cust_main->stateid_state, - 'country' => $cust_main->country, - 'prefix' => 'stateid_', - 'onchange' => $onchange, - 'disabled' => $disabled, - 'style' => \@style, - &> -
-<%$r%><% mt('required fields') |h %>
- -<%def company> -% my $display = ($cust_main->residential_commercial eq 'Commercial') -% ? '' : 'none'; - - <% mt('Company') |h %> - - <%$style%>> - - - - -<%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 phones> - <& /elements/tr-cust_main-phones.html, - 'prefix' => $pre, - 'cust_main' => $cust_main, - 'onchange' => $onchange, - 'disabled' => $disabled, - 'style' => $style, - &> - - -<%def fax> - - <% mt('Fax') |h %> - - <%$style%>> - - - - -<%once> - -my $r = qq!* !; - - -<%shared> - -my( %opt, $cust_main, $pre, $onchange, $disabled, @style, $style ); - - -<%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’s License' - : FS::Msgcat::_gettext('stateid') || 'Driver’s License'; -my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/ - ? 'Driver’s License State' - : FS::Msgcat::_gettext('stateid_state') || 'Driver’s License State'; - -my @invoicing_list = $cust_main->invoicing_list; - -my $agentnum = $cust_main->agentnum if $cust_main->custnum; - - diff --git a/httemplate/edit/cust_main/contacts_new.html b/httemplate/edit/cust_main/contacts_new.html index 0ab02b420..9ccb45f6a 100644 --- a/httemplate/edit/cust_main/contacts_new.html +++ b/httemplate/edit/cust_main/contacts_new.html @@ -1,10 +1,9 @@
-
Contacts <% include('/edit/elements/edit.html', 'embed' => $opt{cust_main}, 'table' => 'cust_main', - 'labels' => { 'contactnum' => 'Contact', + 'labels' => { 'contactnum' => '', #'Contact', #'locationnum' => ' ', }, '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 index 000000000..cf0ada982 --- /dev/null +++ b/httemplate/edit/cust_main/cust_payby.html @@ -0,0 +1,56 @@ +<% include('/edit/elements/edit.html', + 'embed' => $opt{cust_main}, + 'tablenum' => 1, + 'table' => 'cust_main', + 'labels' => { 'custpaybynum' => '', + #'locationnum' => ' ', + }, + '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, + ) +%> +
+<%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; +}; + + + diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index 4d5beee71..5a7920b6f 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -118,6 +118,8 @@ Example: # display, no
, 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; % my $g_row = 0; diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 82ec50c36..52a2608fd 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -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), - ); -} - diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 5f6921c36..7d5d4f398 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -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', diff --git a/httemplate/elements/tr-coords.html b/httemplate/elements/tr-coords.html index 3248dc2de..5ac5ed4e4 100644 --- a/httemplate/elements/tr-coords.html +++ b/httemplate/elements/tr-coords.html @@ -1,6 +1,6 @@ - - <& /elements/tr-select-payby.html, - 'payby_type' => 'cust', - 'multiple' => 1, - 'all_selected' => 1, - &> - - - - - - - - % 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 index 000000000..81a827051 --- /dev/null +++ b/httemplate/search/report_cust_payby.html @@ -0,0 +1,57 @@ +<& /elements/header.html, mt('Customer stored payment infomation report') &> + + + +
<% mt('Latitude') |h %> + <% $latitude %>  <% mt('Longitude') |h %> <% $longitude %> diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html index 503e78274..672c20174 100755 --- a/httemplate/search/cust_main.html +++ b/httemplate/search/cust_main.html @@ -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 index 000000000..140391b15 --- /dev/null +++ b/httemplate/search/cust_payby.html @@ -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", +# +#} + + diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html index cacb7de6e..ba7c99a45 100755 --- a/httemplate/search/report_cust_main.html +++ b/httemplate/search/report_cust_main.html @@ -194,43 +194,6 @@ <% mt('Billing search options') |h %>
<% mt('Payment expiration before') |h %> - - / - -
+ + <& /elements/tr-select-payby.html, + 'payby_type' => 'cust', + 'multiple' => 1, + 'all_selected' => 1, + &> + + + + + + + + +
<% mt('Payment expiration before') |h %> + + / + +
+ +
+ + +
+ +<& /elements/footer.html &> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Advanced customer search'); + + diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index d18c7f70f..081b96bed 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -169,14 +169,12 @@ function areyousure(href, message) { @@ -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' diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html index 64ec591a2..f1125c008 100644 --- a/httemplate/view/cust_main/billing.html +++ b/httemplate/view/cust_main/billing.html @@ -32,165 +32,25 @@ % } % if ( $conf->exists('cust_main-select-billday') -% && ($cust_main->payby eq 'CARD' || $cust_main->payby eq 'CHEK') ) { - - - - -% } - - - - - - - - - -% -%#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 ) = ( '', '' ); -%} -% - - - - - -% if ( $cust_main->paystart_month ) { - - - - -% } elsif ( $cust_main->payissue ) { - - - - -% } - - - - - - -% } 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 %> - - - -% #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'; - - - - - - -% if ( $conf->config('echeck-country') eq 'CA' ) { - - - - -% } - - - - - - - - - - - - -% if ( $conf->exists('show_bankstate') ) { - - - - +% && qsearch({ 'table' => 'cust_payby', +% 'hashref' => { 'custnum' => $cust_main->custnum, }, +% 'extra_sql' => "AND payby IN ( 'CARD', 'CHEK' )", +% 'order_by' => 'LIMIT 1', +% }) +% ) +% { + + + + % } -% } 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 %> - - - - - - -% } elsif ( $cust_main->payby eq 'BILL' ) { - - <% mt('Billing') |h %> - - -% if ( $cust_main->payinfo ) { - - - - - -% } - - - - - -% } elsif ( $cust_main->payby eq 'COMP' ) { - - <% mt('Complimentary') |h %> - - - - - - -% -%#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 ) = ( '', '' ); -%} -% - - - - - +% if ( $cust_main->po_number ) { + + + + % } % my $yes = emt('yes'); @@ -230,13 +90,13 @@ - - - - @@ -336,12 +196,6 @@
- <& cust_main/contacts.html, $cust_main &> + <& cust_main/misc.html, $cust_main &> +
<& cust_main/contacts.html, $cust_main &>
- <& cust_main/misc.html, $cust_main &> -% if ( $conf->config('payby-default') ne 'HIDE' ) { -
<& cust_main/billing.html, $cust_main &> -% } - + <& cust_main/billing.html, $cust_main &> +
<& cust_main/cust_payby.html, $cust_main &>
<% mt('Billing day of month') |h %><% $cust_main->billday %> -
<% mt('Billing type') |h %> -% 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 %> -
<% mt('Card number') |h %><% $cust_main->paymask %>
<% mt('Expiration') |h %><% "$mon/$year" %>
<% mt('Start date') |h %><% $cust_main->paystart_month. '/'. $cust_main->paystart_year %> -
<% mt('Issue #') |h %><% $cust_main->payissue %> -
<% mt('Name on card') |h %><% $cust_main->payname %>
<% mt($routing_label) |h %><% $aba %>
<% mt('Branch number') |h %><% $branch %>
<% mt('Account number') |h %><% $account %>
<% mt('Account type') |h %><% $cust_main->paytype %>
<% mt('Bank name') |h %><% $cust_main->payname %>
<% $paystate_label %><% $cust_main->paystate || '   ' %>
<% mt('Payment day of month') |h %><% $cust_main->billday %> +
<% mt('Phone number') |h %><% $payinfo %>
<% mt('P.O.') |h %><% $cust_main->payinfo %>
<% mt('Attention') |h %><% $cust_main->payname |h %>
<% mt('Authorized by') |h %><% $cust_main->payinfo %>
<% mt('Expiration') |h %><% "$mon/$year" %>
<% mt('Purchase Order #') |h %><% $cust_main->po_number %>
<% mt('Postal mail invoices') |h %> - <% ( grep { $_ eq 'POST' } @invoicing_list ) ? $yes : $no %> -
<% mt('Fax invoices') |h %> - <% ( grep { $_ eq 'FAX' } @invoicing_list ) ? $yes : $no %> + <% ( grep { $_ eq 'POST' } @invoicing_list ) + ? $yes. ( $cust_main->invoice_attn + ? ', attn: '. $cust_main->invoice_attn + : '' + ) + : $no + %>
-<%once> - -my $paystate_label = FS::Msgcat::_gettext('paystate'); -$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/; - - <%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 index 000000000..3ebb0551d --- /dev/null +++ b/httemplate/view/cust_main/cust_payby.html @@ -0,0 +1,133 @@ +% if ( @cust_payby ) { + + <% mt('Payment information') |h %> + + +% 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'; + + + + + + + + +% my( $mon, $year ) = $cust_payby->paydate_mon_year; + + + + + +% if ( $cust_payby->paystart_month ) { + + + +% } elsif ( $cust_payby->payissue ) { + + + +% } + + + + + + +% } 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'; + + + + + +% #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'; + + + + + +% if ( $conf->config('echeck-country') eq 'CA' ) { + + + + +% } + + + + + + + + + + + + + + +% if ( $conf->exists('show_bankstate') ) { + + + + +% } + +% } else { + + + +% } + +% unless ( $num++ == $#cust_payby ) { + + + + + + +% } + +% } + +
+ <% mt("Credit card ([_1])",$auto) |h %> +
<% mt('Card number') |h %><% $cust_payby->paymask %>
<% mt('Expiration') |h %><% "$mon/$year" %>
<% mt('Start date') |h %><% $cust_payby->paystart_month. '/'. $cust_payby->paystart_year %> +
<% mt('Issue #') |h %><% $cust_payby->payissue %> +
<% mt('Name on card') |h %><% $cust_payby->payname %>
+ <% mt("Electronic check ([_1])",$auto) |h %> +
<% mt($routing_label) |h %><% $aba %>
<% mt('Branch number') |h %><% $branch %>
<% mt('Account number') |h %><% $account %>
<% mt('Account type') |h %><% $cust_payby->paytype %>
<% mt('Bank name') |h %><% $cust_payby->payname %>
<% $paystate_label %><% $cust_payby->paystate || '   ' %>
+ Unknown cust_pay.payby <% $cust_payby->payby %> +
+ +% } +<%once> + +my $paystate_label = FS::Msgcat::_gettext('paystate'); +$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/; + + +<%init> + +my( $cust_main ) = @_; +my $conf = new FS::Conf; +my @cust_payby = $cust_main->cust_payby; + + diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 9a2332ba8..90985110c 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -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 &> % } -- 2.11.0