X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=4316988ca773e84a078e62b29b0317bfa4d93454;hb=b5124265c3f3781d0f961b836cbf674fde12ce54;hp=94fd97f3926bb21e229eb488243453282da05c95;hpb=88d4198ff452581be05e3018b3e23db564545525;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 94fd97f39..4316988ca 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -26,6 +26,7 @@ use FS::queue; use FS::part_pkg; use FS::part_bill_event; use FS::cust_bill_event; +use FS::Msgcat qw(gettext); @ISA = qw( FS::Record ); @@ -99,7 +100,7 @@ FS::Record. The following fields are currently supported: =item agentnum - agent (see L) -=item refnum - referral (see L) +=item refnum - Advertising source (see L) =item first - name @@ -255,7 +256,8 @@ sub insert { my $error = $self->SUPER::insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "inserting cust_main record (transaction rolled back): $error"; + #return "inserting cust_main record (transaction rolled back): $error"; + return $error; } if ( @param ) { # CUST_PKG_HASHREF @@ -276,7 +278,8 @@ sub insert { $error = $svc_something->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "inserting svc_ (transaction rolled back): $error"; + #return "inserting svc_ (transaction rolled back): $error"; + return $error; } } } @@ -512,6 +515,8 @@ and repalce methods. sub check { my $self = shift; + #warn "BEFORE: \n". $self->_dump; + my $error = $self->ut_numbern('custnum') || $self->ut_number('agentnum') @@ -529,14 +534,14 @@ sub check { || $self->ut_numbern('referral_custnum') ; #barf. need message catalogs. i18n. etc. - $error .= "Please select a referral." + $error .= "Please select a advertising source." if $error =~ /^Illegal or empty \(numeric\) refnum: /; return $error if $error; return "Unknown agent" unless qsearchs( 'agent', { 'agentnum' => $self->agentnum } ); - return "Unknown referral" + return "Unknown refnum" unless qsearchs( 'part_referral', { 'refnum' => $self->refnum } ); return "Unknown referring custnum ". $self->referral_custnum @@ -553,7 +558,9 @@ sub check { $self->ss("$1-$2-$3"); } - unless ( $import ) { + +# bad idea to disable, causes billing to fail because of no tax rates later +# unless ( $import ) { unless ( qsearchs('cust_main_county', { 'country' => $self->country, 'state' => '', @@ -566,7 +573,7 @@ sub check { 'country' => $self->country, } ); } - } +# } $error = $self->ut_phonen('daytime', $self->country) @@ -582,8 +589,9 @@ sub check { ); if ( defined $self->dbdef_table->column('ship_last') ) { - if ( grep { $self->getfield($_) ne $self->getfield("ship_$_") } @addfields - && grep $self->getfield("ship_$_"), grep $_ ne 'state', @addfields + if ( scalar ( grep { $self->getfield($_) ne $self->getfield("ship_$_") } + @addfields ) + && scalar ( grep { $self->getfield("ship_$_") ne '' } @addfields ) ) { my $error = @@ -638,12 +646,13 @@ sub check { my $payinfo = $self->payinfo; $payinfo =~ s/\D//g; $payinfo =~ /^(\d{13,16})$/ - or return "Illegal credit card number: ". $self->payinfo; + or return gettext('invalid_card'); # . ": ". $self->payinfo; $payinfo = $1; $self->payinfo($payinfo); validate($payinfo) - or return "Illegal credit card number: ". $self->payinfo; - return "Unknown card type" if cardtype($self->payinfo) eq "Unknown"; + or return gettext('invalid_card'); # . ": ". $self->payinfo; + return gettext('unknown_card_type') + if cardtype($self->payinfo) eq "Unknown"; } elsif ( $self->payby eq 'BILL' ) { @@ -674,18 +683,18 @@ sub check { } else { $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ or return "Illegal expiration date: ". $self->paydate; - if ( length($2) == 4 ) { - $self->paydate("$2-$1-01"); - } else { - $self->paydate("20$2-$1-01"); - } + my $y = length($2) == 4 ? $2 : "20$2"; + $self->paydate("$y-$1-01"); + my($nowm,$nowy)=(localtime(time))[4,5]; $nowm++; $nowy+=1900; + return gettext('expired_card') if $y<$nowy || ( $y==$nowy && $1<$nowm ); } - if ( $self->payname eq '' ) { + if ( $self->payname eq '' && + ( ! $conf->exists('require_cardname') || $self->payby ne 'CARD' ) ) { $self->payname( $self->first. " ". $self->getfield('last') ); } else { $self->payname =~ /^([\w \,\.\-\']+)$/ - or return "Illegal billing name: ". $self->payname; + or return gettext('illegal_name'). " payname: ". $self->payname; $self->payname($1); } @@ -694,6 +703,8 @@ sub check { $self->otaker(getotaker); + #warn "AFTER: \n". $self->_dump; + ''; #no error } @@ -810,6 +821,17 @@ sub cancel { grep { $_->cancel } $self->ncancelled_pkgs; } +=item agent + +Returns the agent (see L) for this customer. + +=cut + +sub agent { + my $self = shift; + qsearchs( 'agent', { 'agentnum' => $self->agentnum } ); +} + =item bill OPTIONS Generates invoices (see L) for this customer. Usually used in @@ -913,6 +935,9 @@ sub bill { }; $recur_prog = $1; + # shared with $recur_prog + $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; + #my $cpt = new Safe; ##$cpt->permit(); #what is necessary? #$cpt->share(qw( $cust_pkg )); #can $cpt now use $cust_pkg methods? @@ -925,11 +950,14 @@ sub bill { } #change this bit to use Date::Manip? CAREFUL with timezones (see # mailing list archive) - #$sdate=$cust_pkg->bill || time; - #$sdate=$cust_pkg->bill || $time; - $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; + + #pro-rating magic - if $recur_prog fiddles $sdate, want to use that + # only for figuring next bill date, nothing else, so, reset $sdate again + # here + $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; + $mon += $part_pkg->getfield('freq'); until ( $mon < 12 ) { $mon -= 12; $year++; } $cust_pkg->setfield('bill', @@ -970,10 +998,10 @@ sub bill { $total_recur += $recur; $taxable_setup += $setup unless $part_pkg->dbdef_table->column('setuptax') - || $part_pkg->setuptax =~ /^Y$/i; + && $part_pkg->setuptax =~ /^Y$/i; $taxable_recur += $recur unless $part_pkg->dbdef_table->column('recurtax') - || $part_pkg->recurtax =~ /^Y$/i; + && $part_pkg->recurtax =~ /^Y$/i; } } @@ -994,7 +1022,8 @@ sub bill { 'state' => $self->state, 'county' => $self->county, 'country' => $self->country, - } ); + } ) or die "fatal: can't find tax rate for state/county/country ". + $self->state. "/". $self->county. "/". $self->country. "\n"; my $tax = sprintf( "%.2f", $taxable_charged * ( $cust_main_county->getfield('tax') / 100 ) ); @@ -1088,7 +1117,7 @@ sub collect { my $dbh = dbh; my $balance = $self->balance; - warn "collect: balance $balance" if $Debug; + warn "collect customer". $self->custnum. ": balance $balance" if $Debug; unless ( $balance > 0 ) { #redundant????? $dbh->rollback if $oldAutoCommit; #hmm return ''; @@ -1118,48 +1147,60 @@ sub collect { sort { $a->seconds <=> $b->seconds || $a->weight <=> $b->weight || $a->eventpart <=> $b->eventpart } - grep { $_->seconds > ( $invoice_time - ( $cust_bill->_date || 0 ) ) + grep { $_->seconds <= ( $invoice_time - $cust_bill->_date ) && ! qsearchs( 'cust_bill_event', { 'invnum' => $cust_bill->invnum, - 'eventpart' => $_->eventpart } ) + 'eventpart' => $_->eventpart, + 'status' => 'done', + } ) } qsearch('part_bill_event', { 'payby' => $self->payby, 'disabled' => '', } ) ) { - #run callback - my $cust_main = $self; #for callback - my $error = eval $part_bill_event->eventcode; - if ( $error ) { + last unless $cust_bill->owed > 0; #don't run subsequent events if owed=0 - warn "Error running invoice event (". $part_bill_event->eventcode. - "): $error"; + warn "calling invoice event (". $part_bill_event->eventcode. ")\n" + if $Debug; + my $cust_main = $self; #for callback + my $error = eval $part_bill_event->eventcode; + my $status = ''; + my $statustext = ''; + if ( $@ ) { + $status = 'failed'; + $statustext = $@; + } elsif ( $error ) { + $status = 'done'; + $statustext = $error; } else { + $status = 'done' + } - #add cust_bill_event - my $cust_bill_event = new FS::cust_bill_event { - 'invnum' => $cust_bill->invnum, - 'eventpart' => $part_bill_event->eventpart, - '_date' => $invoice_time, - }; - $cust_bill_event->insert; - if ( $error ) { - #$dbh->rollback if $oldAutoCommit; - #return "error: $error"; - - # gah, even with transactions. - $dbh->commit if $oldAutoCommit; #well. - my $e = 'WARNING: Event run but database not updated - '. - 'error inserting cust_bill_event, invnum #'. $cust_bill->invnum. - ', eventpart '. $part_bill_event->eventpart. - ": $error"; - warn $e; - return $e; - } - + #add cust_bill_event + my $cust_bill_event = new FS::cust_bill_event { + 'invnum' => $cust_bill->invnum, + 'eventpart' => $part_bill_event->eventpart, + '_date' => $invoice_time, + 'status' => $status, + 'statustext' => $statustext, + }; + $error = $cust_bill_event->insert; + if ( $error ) { + #$dbh->rollback if $oldAutoCommit; + #return "error: $error"; + + # gah, even with transactions. + $dbh->commit if $oldAutoCommit; #well. + my $e = 'WARNING: Event run but database not updated - '. + 'error inserting cust_bill_event, invnum #'. $cust_bill->invnum. + ', eventpart '. $part_bill_event->eventpart. + ": $error"; + warn $e; + return $e; } + } } @@ -1518,11 +1559,23 @@ sub referral_cust_main { @cust_main; } +=item referral_cust_main_ncancelled + +Same as referral_cust_main, except only returns customers with uncancelled +packages. + +=cut + +sub referral_cust_main_ncancelled { + my $self = shift; + grep { scalar($_->ncancelled_pkgs) } $self->referral_cust_main; +} + =item referral_cust_pkg [ DEPTH ] -Like referral_cust_main, except returns a flat list of all unsuspended packages -for each customer. The number of items in this list may be useful for -comission calculations (perhaps after a grep). +Like referral_cust_main, except returns a flat list of all unsuspended (and +uncancelled) packages for each customer. The number of items in this list may +be useful for comission calculations (perhaps after a Cpkgpart; grep { $_ == $pkgpart } @commission_worthy_pkgparts> } $cust_main-> ). =cut @@ -1564,7 +1617,7 @@ sub charge { my $part_pkg = new FS::part_pkg ( { 'pkg' => $pkg || 'One-time charge', - 'comment' => $comment, + 'comment' => $comment || '$'. sprintf("%.2f".$amount), 'setup' => $amount, 'freq' => 0, 'recur' => '0',