X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=a62987b3273d2a886da3a9f9708c8cf0f0d99439;hb=8c804ba59c8b284e1294e9928b5412fdf31e412c;hp=27a617e1aecc20264ae72ccbbcf2c6d8a62327b2;hpb=fd1bf67102a849280d78a041eff33e87c3cc7179;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 27a617e1a..a62987b32 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -76,6 +76,8 @@ $skip_fuzzyfiles = 0; $ignore_expired_card = 0; @encrypted_fields = ('payinfo', 'paycvv'); +sub nohistory_fields { ('paycvv'); } + @paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings'); #ask FS::UID to run this stuff for us later @@ -1809,7 +1811,7 @@ sub has_ship_address { scalar( grep { $self->getfield("ship_$_") ne '' } $self->addr_fields ); } -=item all_pkgs +=item all_pkgs [ EXTRA_QSEARCH_PARAMS_HASHREF ] Returns all packages (see L) for this customer. @@ -1817,14 +1819,15 @@ Returns all packages (see L) for this customer. sub all_pkgs { my $self = shift; + my $extra_qsearch = ref($_[0]) ? shift : {}; - return $self->num_pkgs unless wantarray; + return $self->num_pkgs unless wantarray || keys(%$extra_qsearch); my @cust_pkg = (); if ( $self->{'_pkgnum'} ) { @cust_pkg = values %{ $self->{'_pkgnum'}->cache }; } else { - @cust_pkg = qsearch( 'cust_pkg', { 'custnum' => $self->custnum }); + @cust_pkg = $self->_cust_pkg($extra_qsearch); } sort sort_packages @cust_pkg; @@ -1851,7 +1854,7 @@ sub cust_location { qsearch('cust_location', { 'custnum' => $self->custnum } ); } -=item ncancelled_pkgs +=item ncancelled_pkgs [ EXTRA_QSEARCH_PARAMS_HASHREF ] Returns all non-cancelled packages (see L) for this customer. @@ -1859,6 +1862,7 @@ Returns all non-cancelled packages (see L) for this customer. sub ncancelled_pkgs { my $self = shift; + my $extra_qsearch = ref($_[0]) ? shift : {}; return $self->num_ncancelled_pkgs unless wantarray; @@ -1877,33 +1881,56 @@ sub ncancelled_pkgs { $self->custnum. "\n" if $DEBUG > 1; - @cust_pkg = - qsearch( 'cust_pkg', { - 'custnum' => $self->custnum, - 'cancel' => '', - }); - push @cust_pkg, - qsearch( 'cust_pkg', { - 'custnum' => $self->custnum, - 'cancel' => 0, - }); + $extra_qsearch->{'extra_sql'} .= ' AND ( cancel IS NULL OR cancel = 0 ) '; + + @cust_pkg = $self->_cust_pkg($extra_qsearch); + } sort sort_packages @cust_pkg; } +sub _cust_pkg { + my $self = shift; + my $extra_qsearch = ref($_[0]) ? shift : {}; + + $extra_qsearch->{'select'} ||= '*'; + $extra_qsearch->{'select'} .= + ',( SELECT COUNT(*) FROM cust_svc WHERE cust_pkg.pkgnum = cust_svc.pkgnum ) + AS _num_cust_svc'; + + map { + $_->{'_num_cust_svc'} = $_->get('_num_cust_svc'); + $_; + } + qsearch({ + %$extra_qsearch, + 'table' => 'cust_pkg', + 'hashref' => { 'custnum' => $self->custnum }, + }); + +} + # This should be generalized to use config options to determine order. sub sort_packages { - if ( $a->get('cancel') and $b->get('cancel') ) { - $a->pkgnum <=> $b->pkgnum; - } elsif ( $a->get('cancel') or $b->get('cancel') ) { + + if ( $a->get('cancel') xor $b->get('cancel') ) { return -1 if $b->get('cancel'); return 1 if $a->get('cancel'); + #shouldn't get here... return 0; } else { - $a->pkgnum <=> $b->pkgnum; + my $a_num_cust_svc = $a->num_cust_svc; + my $b_num_cust_svc = $b->num_cust_svc; + return 0 if !$a_num_cust_svc && !$b_num_cust_svc; + return -1 if $a_num_cust_svc && !$b_num_cust_svc; + return 1 if !$a_num_cust_svc && $b_num_cust_svc; + my @a_cust_svc = $a->cust_svc; + my @b_cust_svc = $b->cust_svc; + $a_cust_svc[0]->svc_x->label cmp $b_cust_svc[0]->svc_x->label; } + } =item suspended_pkgs @@ -2432,6 +2459,7 @@ sub bill { foreach my $tax ( keys %taxlisthash ) { my $tax_object = shift @{ $taxlisthash{$tax} }; warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2; + warn " ". join('/', @{ $taxlisthash{$tax} } ). "\n" if $DEBUG > 2; my $hashref_or_error = $tax_object->taxline( $taxlisthash{$tax}, 'custnum' => $self->custnum, @@ -2508,20 +2536,20 @@ sub bill { my $tax_object = shift @{ $totlisthash{$tax} }; warn "found previously found taxed tax ". $tax_object->taxname. "\n" if $DEBUG > 2; - my $listref_or_error = + my $hashref_or_error = $tax_object->taxline( $totlisthash{$tax}, 'custnum' => $self->custnum, 'invoice_time' => $invoice_time ); - unless (ref($listref_or_error)) { + unless (ref($hashref_or_error)) { $dbh->rollback if $oldAutoCommit; - return $listref_or_error; + return $hashref_or_error; } - warn "adding taxed tax amount ". $listref_or_error->[1]. + warn "adding taxed tax amount ". $hashref_or_error->{'amount'}. " as ". $tax_object->taxname. "\n" if $DEBUG; - $tax{ $tax } += $listref_or_error->[1]; + $tax{ $tax } += $hashref_or_error->{'amount'}; } #consolidate and create tax line items @@ -2914,7 +2942,7 @@ sub _handle_taxes { foreach my $tax ( @taxes ) { - my $taxname = ref( $tax ). ' taxnum'. $tax->taxnum; + my $taxname = ref( $tax ). ' '. $tax->taxnum; # $taxname .= ' pkgnum'. $cust_pkg->pkgnum. # ' locationnum'. $cust_pkg->locationnum # if $conf->exists('tax-pkg_address') && $cust_pkg->locationnum; @@ -3475,24 +3503,35 @@ sub realtime_bop { return "Banned credit card" if $ban; ### - # select a gateway + # set taxclass and trans_is_recur based on invnum if there is one ### my $taxclass = ''; + my $trans_is_recur = 0; if ( $options{'invnum'} ) { + my $cust_bill = qsearchs('cust_bill', { 'invnum' => $options{'invnum'} } ); die "invnum ". $options{'invnum'}. " not found" unless $cust_bill; - my @taxclasses = - map { $_->part_pkg->taxclass } + + my @part_pkg = + map { $_->part_pkg } grep { $_ } map { $_->cust_pkg } $cust_bill->cust_bill_pkg; - unless ( grep { $taxclasses[0] ne $_ } @taxclasses ) { #unless there are - #different taxclasses - $taxclass = $taxclasses[0]; - } + + my @taxclasses = map $_->taxclass, @part_pkg; + $taxclass = $taxclasses[0] + unless grep { $taxclasses[0] ne $_ } @taxclasses; #unless there are + #different taxclasses + $trans_is_recur = 1 + if grep { $_->freq ne '0' } @part_pkg; + } + ### + # select a gateway + ### + #look for an agent gateway override first my $cardtype; if ( $method eq 'CC' ) { @@ -3620,16 +3659,15 @@ sub realtime_bop { : $self->payissue; $content{issue_number} = $payissue if $payissue; - $content{recurring_billing} = 'YES' - if qsearch('cust_pay', { 'custnum' => $self->custnum, - 'payby' => 'CARD', - 'payinfo' => $payinfo, - } ) - || qsearch('cust_pay', { 'custnum' => $self->custnum, - 'payby' => 'CARD', - 'paymask' => $self->mask_payinfo('CARD', $payinfo), - } ); - + if ( $self->_bop_recurring_billing( 'payinfo' => $payinfo, + 'trans_is_recur' => $trans_is_recur, + ) + ) + { + $content{recurring_billing} = 'YES'; + $content{acct_code} = 'rebill' + if $conf->exists('credit_card-recurring_billing_acct_code'); + } } elsif ( $method eq 'ECHECK' ) { ( $content{account_number}, $content{routing_code} ) = @@ -3688,15 +3726,16 @@ sub realtime_bop { #okay, good to go, if we're a duplicate, cust_pay_pending will kick us out my $cust_pay_pending = new FS::cust_pay_pending { - 'custnum' => $self->custnum, - #'invnum' => $options{'invnum'}, - 'paid' => $amount, - '_date' => '', - 'payby' => $method2payby{$method}, - 'payinfo' => $payinfo, - 'paydate' => $paydate, - 'status' => 'new', - 'gatewaynum' => ( $payment_gateway ? $payment_gateway->gatewaynum : '' ), + 'custnum' => $self->custnum, + #'invnum' => $options{'invnum'}, + 'paid' => $amount, + '_date' => '', + 'payby' => $method2payby{$method}, + 'payinfo' => $payinfo, + 'paydate' => $paydate, + 'recurring_billing' => $content{recurring_billing}, + 'status' => 'new', + 'gatewaynum' => ( $payment_gateway ? $payment_gateway->gatewaynum : '' ), }; $cust_pay_pending->payunique( $options{payunique} ) if defined($options{payunique}) && length($options{payunique}); @@ -3982,6 +4021,34 @@ sub realtime_bop { } +sub _bop_recurring_billing { + my( $self, %opt ) = @_; + + my $method = $conf->config('credit_card-recurring_billing_flag'); + + if ( $method eq 'transaction_is_recur' ) { + + return 1 if $opt{'trans_is_recur'}; + + } else { + + my %hash = ( 'custnum' => $self->custnum, + 'payby' => 'CARD', + ); + + return 1 + if qsearch('cust_pay', { %hash, 'payinfo' => $opt{'payinfo'} } ) + || qsearch('cust_pay', { %hash, 'paymask' => $self->mask_payinfo('CARD', + $opt{'payinfo'} ) + } ); + + } + + return 0; + +} + + =item realtime_refund_bop METHOD [ OPTION => VALUE ... ] Refunds a realtime credit card, ACH (electronic check) or phone bill transaction @@ -4534,6 +4601,27 @@ sub _new_realtime_bop { $self->_bop_defaults(\%options); ### + # set trans_is_recur based on invnum if there is one + ### + + my $trans_is_recur = 0; + if ( $options{'invnum'} ) { + + my $cust_bill = qsearchs('cust_bill', { 'invnum' => $options{'invnum'} } ); + die "invnum ". $options{'invnum'}. " not found" unless $cust_bill; + + my @part_pkg = + map { $_->part_pkg } + grep { $_ } + map { $_->cust_pkg } + $cust_bill->cust_bill_pkg; + + $trans_is_recur = 1 + if grep { $_->freq ne '0' } @part_pkg; + + } + + ### # select a gateway ### @@ -4609,16 +4697,15 @@ sub _new_realtime_bop { : $self->payissue; $content{issue_number} = $payissue if $payissue; - $content{recurring_billing} = 'YES' - if qsearch('cust_pay', { 'custnum' => $self->custnum, - 'payby' => 'CARD', - 'payinfo' => $options{payinfo}, - } ) - || qsearch('cust_pay', { 'custnum' => $self->custnum, - 'payby' => 'CARD', - 'paymask' => $self->mask_payinfo('CARD', $options{payinfo}), - } ); - + if ( $self->_bop_recurring_billing( 'payinfo' => $options{'payinfo'}, + 'trans_is_recur' => $trans_is_recur, + ) + ) + { + $content{recurring_billing} = 'YES'; + $content{acct_code} = 'rebill' + if $conf->exists('credit_card-recurring_billing_acct_code'); + } } elsif ( $namespace eq 'Business::OnlinePayment' && $options{method} eq 'ECHECK' ){ ( $content{account_number}, $content{routing_code} ) = @@ -4680,17 +4767,18 @@ sub _new_realtime_bop { #okay, good to go, if we're a duplicate, cust_pay_pending will kick us out my $cust_pay_pending = new FS::cust_pay_pending { - 'custnum' => $self->custnum, - #'invnum' => $options{'invnum'}, - 'paid' => $options{amount}, - '_date' => '', - 'payby' => $bop_method2payby{$options{method}}, - 'payinfo' => $options{payinfo}, - 'paydate' => $paydate, - 'status' => 'new', - 'gatewaynum' => $payment_gateway->gatewaynum || '', - 'session_id' => $options{session_id} || '', - 'jobnum' => $options{depend_jobnum} || '', + 'custnum' => $self->custnum, + #'invnum' => $options{'invnum'}, + 'paid' => $options{amount}, + '_date' => '', + 'payby' => $bop_method2payby{$options{method}}, + 'payinfo' => $options{payinfo}, + 'paydate' => $paydate, + 'recurring_billing' => $content{recurring_billing}, + 'status' => 'new', + 'gatewaynum' => $payment_gateway->gatewaynum || '', + 'session_id' => $options{session_id} || '', + 'jobnum' => $options{depend_jobnum} || '', }; $cust_pay_pending->payunique( $options{payunique} ) if defined($options{payunique}) && length($options{payunique}); @@ -8404,6 +8492,15 @@ sub queued_bill { ); } +sub _upgrade_data { #class method + my ($class, %opts) = @_; + + my $sql = 'UPDATE h_cust_main SET paycvv = NULL WHERE paycvv IS NOT NULL'; + my $sth = dbh->prepare($sql) or die dbh->errstr; + $sth->execute or die $sth->errstr; + +} + =back =head1 BUGS