X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=3e767d9f0277f9cb844d8294c2d79215127799f5;hb=3bd1b2b68adbb67f90addd668132d3d3e9adb698;hp=88d8bdd9157b24da8e1aa9b2b4d1a533e9749ebb;hpb=808b89d9c1f34c9c66064da212ab7036b85973a1;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 88d8bdd91..3e767d9f0 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -227,6 +227,8 @@ Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit nu =item spool_cdr - Enable individual CDR spooling, empty or `Y' +=item dundate - a suggestion to events (see L) to delay until this unix timestamp + =item squelch_cdr - Discourage individual CDR printing, empty or `Y' =back @@ -341,6 +343,9 @@ sub insert { $self->signupdate(time) unless $self->signupdate; + $self->auto_agent_custid() + if $conf->config('cust_main-auto_agent_custid') && ! $self->agent_custid; + my $error = $self->SUPER::insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; @@ -417,6 +422,35 @@ sub insert { } +use File::CounterFile; +sub auto_agent_custid { + my $self = shift; + + my $format = $conf->config('cust_main-auto_agent_custid'); + my $agent_custid; + if ( $format eq '1YMMXXXXXXXX' ) { + + my $counter = new File::CounterFile 'cust_main.agent_custid'; + $counter->lock; + + my $ym = 100000000000 + time2str('%y%m00000000', time); + if ( $ym > $counter->value ) { + $counter->{'value'} = $agent_custid = $ym; + $counter->{'updated'} = 1; + } else { + $agent_custid = $counter->inc; + } + + $counter->unlock; + + } else { + die "Unknown cust_main-auto_agent_custid format: $format"; + } + + $self->agent_custid($agent_custid); + +} + sub start_copy_skel { my $self = shift; @@ -1231,6 +1265,7 @@ sub check { || $self->ut_textn('stateid_state') || $self->ut_textn('invoice_terms') ; + #barf. need message catalogs. i18n. etc. $error .= "Please select an advertising source." if $error =~ /^Illegal or empty \(numeric\) refnum: /; @@ -1962,7 +1997,12 @@ sub bill_and_collect { $self->ncancelled_pkgs; foreach my $cust_pkg ( @cancel_pkgs ) { - my $error = $cust_pkg->cancel; + my $cpr = $cust_pkg->last_cust_pkg_reason('expire'); + my $error = $cust_pkg->cancel($cpr ? ( 'reason' => $cpr->reasonnum, + 'reason_otaker' => $cpr->otaker + ) + : () + ); warn "Error cancelling expired pkg ". $cust_pkg->pkgnum. " for custnum ". $self->custnum. ": $error" if $error; @@ -1988,7 +2028,14 @@ sub bill_and_collect { $self->ncancelled_pkgs; foreach my $cust_pkg ( @susp_pkgs ) { - my $error = $cust_pkg->suspend; + my $cpr = $cust_pkg->last_cust_pkg_reason('adjourn') + if ($cust_pkg->adjourn && $cust_pkg->adjourn < $^T); + my $error = $cust_pkg->suspend($cpr ? ( 'reason' => $cpr->reasonnum, + 'reason_otaker' => $cpr->otaker + ) + : () + ); + warn "Error suspending package ". $cust_pkg->pkgnum. " for custnum ". $self->custnum. ": $error" if $error; @@ -2133,7 +2180,12 @@ sub bill { return "can't charge postal invoice fee for customer ". $self->custnum. ": $postal_pkg"; } - if ( $postal_pkg ) { + if ( $postal_pkg && + ( scalar( grep { $_->recur && $_->recur > 0 } @cust_bill_pkg) || + !$conf->exists('postal_invoice-recurring_only') + ) + ) + { foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) { my $error = $self->_make_lines( 'part_pkg' => $part_pkg, @@ -2347,9 +2399,13 @@ sub _make_lines { my $recur = 0; my $unitrecur = 0; my $sdate; - if ( $part_pkg->getfield('freq') ne '0' && - ! $cust_pkg->getfield('susp') && - ( $cust_pkg->getfield('bill') || 0 ) <= $time + if ( ! $cust_pkg->getfield('susp') and + ( $part_pkg->getfield('freq') ne '0' && + ( $cust_pkg->getfield('bill') || 0 ) <= $time + ) + || ( $part_pkg->plan eq 'voip_cdr' + && $part_pkg->option('bill_every_call') + ) ) { # XXX should this be a package event? probably. events are called @@ -2366,42 +2422,50 @@ sub _make_lines { $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; #over two params! lets at least switch to a hashref for the rest... - my %param = ( 'precommit_hooks' => $precommit_hooks, ); + my $increment_next_bill = ( $part_pkg->freq ne '0' + && ( $cust_pkg->getfield('bill') || 0 ) <= $time + ); + my %param = ( 'precommit_hooks' => $precommit_hooks, + 'increment_next_bill' => $increment_next_bill, + ); $recur = eval { $cust_pkg->calc_recur( \$sdate, \@details, \%param ) }; return "$@ running calc_recur for $cust_pkg\n" if ( $@ ); + if ( $increment_next_bill ) { - #change this bit to use Date::Manip? CAREFUL with timezones (see - # mailing list archive) - 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; - #no need, its in $hash{last_bill}# my $last_bill = $cust_pkg->last_bill; - $cust_pkg->last_bill($sdate); - - if ( $part_pkg->freq =~ /^\d+$/ ) { - $mon += $part_pkg->freq; - until ( $mon < 12 ) { $mon -= 12; $year++; } - } elsif ( $part_pkg->freq =~ /^(\d+)w$/ ) { - my $weeks = $1; - $mday += $weeks * 7; - } elsif ( $part_pkg->freq =~ /^(\d+)d$/ ) { - my $days = $1; - $mday += $days; - } elsif ( $part_pkg->freq =~ /^(\d+)h$/ ) { - my $hours = $1; - $hour += $hours; - } else { - return "unparsable frequency: ". $part_pkg->freq; + #change this bit to use Date::Manip? CAREFUL with timezones (see + # mailing list archive) + 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; + #no need, its in $hash{last_bill}# my $last_bill = $cust_pkg->last_bill; + $cust_pkg->last_bill($sdate); + + if ( $part_pkg->freq =~ /^\d+$/ ) { + $mon += $part_pkg->freq; + until ( $mon < 12 ) { $mon -= 12; $year++; } + } elsif ( $part_pkg->freq =~ /^(\d+)w$/ ) { + my $weeks = $1; + $mday += $weeks * 7; + } elsif ( $part_pkg->freq =~ /^(\d+)d$/ ) { + my $days = $1; + $mday += $days; + } elsif ( $part_pkg->freq =~ /^(\d+)h$/ ) { + my $hours = $1; + $hour += $hours; + } else { + return "unparsable frequency: ". $part_pkg->freq; + } + $cust_pkg->setfield('bill', + timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year)); + } - $cust_pkg->setfield('bill', - timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year)); } @@ -2460,10 +2524,10 @@ sub _make_lines { 'details' => \@details, }; - if ( $part_pkg->option('recur_temporality') eq 'preceding' ) { + if ( $part_pkg->option('recur_temporality', 1) eq 'preceding' ) { $cust_bill_pkg->sdate( $hash{last_bill} ); $cust_bill_pkg->edate( $sdate - 86399 ); #60s*60m*24h-1 - } else { #if ( $part_pkg->option('recur_temporality') eq 'upcoming' ) { + } else { #if ( $part_pkg->option('recur_temporality', 1) eq 'upcoming' ) { $cust_bill_pkg->sdate( $sdate ); $cust_bill_pkg->edate( $cust_pkg->bill ); } @@ -2964,14 +3028,16 @@ sub due_cust_event { # 3: insert ## - foreach my $cust_event ( @cust_event ) { + unless( $opt{testonly} ) { + foreach my $cust_event ( @cust_event ) { - my $error = $cust_event->insert(); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return $error; - } + my $error = $cust_event->insert(); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } } $dbh->commit or die $dbh->errstr if $oldAutoCommit; @@ -5030,6 +5096,22 @@ sub cust_refund { qsearch( 'cust_refund', { 'custnum' => $self->custnum } ) } +=item display_custnum + +Returns the displayed customer number for this customer: agent_custid if +cust_main-default_agent_custid is set and it has a value, custnum otherwise. + +=cut + +sub display_custnum { + my $self = shift; + if ( $conf->exists('cust_main-default_agent_custid') && $self->agent_custid ){ + return $self->agent_custid; + } else { + return $self->custnum; + } +} + =item name Returns a name string for this customer, either "Company (Last, First)" or @@ -5118,15 +5200,16 @@ sub geocode { my $extra_sql = "AND plus4lo <= '$plus4' AND plus4hi >= '$plus4'"; my $geocode = ''; - my $cust_tax_location = - qsearchs( { - 'table' => 'cust_tax_location', - 'hashref' => { 'zip' => $zip, 'data_vendor' => $data_vendor }, - 'extra_sql' => $extra_sql, - } - ); - $geocode = $cust_tax_location->geocode - if $cust_tax_location; + my @cust_tax_location = + qsearch( { + 'table' => 'cust_tax_location', + 'hashref' => { 'zip' => $zip, 'data_vendor' => $data_vendor }, + 'extra_sql' => $extra_sql, + 'order_by' => 'ORDER BY plus4hi',#overlapping with distinct ends + } + ); + $geocode = $cust_tax_location[0]->geocode + if scalar(@cust_tax_location); $geocode; } @@ -5917,22 +6000,28 @@ sub smart_search { # custnum search (also try agent_custid), with some tweaking options if your # legacy cust "numbers" have letters - } elsif ( $search =~ /^\s*(\d+)\s*$/ + } + + if ( $search =~ /^\s*(\d+)\s*$/ || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+' && $search =~ /^\s*(\w\w?\d+)\s*$/ ) ) { - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { 'custnum' => $1, %options }, - 'extra_sql' => " AND $agentnums_sql", #agent virtualization - } ); + my $num = $1; + + if ( $num <= 2147483647 ) { #need a bigint custnum? wow. + push @cust_main, qsearch( { + 'table' => 'cust_main', + 'hashref' => { 'custnum' => $num, %options }, + 'extra_sql' => " AND $agentnums_sql", #agent virtualization + } ); + } push @cust_main, qsearch( { 'table' => 'cust_main', - 'hashref' => { 'agent_custid' => $1, %options }, + 'hashref' => { 'agent_custid' => $num, %options }, 'extra_sql' => " AND $agentnums_sql", #agent virtualization } ); @@ -6322,6 +6411,9 @@ sub process_batch_import { =cut +use FS::svc_acct; +use FS::svc_external; + #some false laziness w/cdr.pm now sub batch_import { my $param = shift; @@ -6369,6 +6461,18 @@ sub batch_import { svc_acct.username svc_acct._password ); $payby = 'BILL'; + } elsif ( $format eq 'svc_external' ) { + @fields = qw( agent_custid refnum + last first company address1 address2 city state zip country + daytime night + ship_last ship_first ship_company ship_address1 ship_address2 + ship_city ship_state ship_zip ship_country + payinfo paycvv paydate + invoicing_list + cust_pkg.pkgpart cust_pkg.bill + svc_external.id svc_external.title + ); + $payby = 'BILL'; } else { die "unknown format $format"; } @@ -6457,7 +6561,7 @@ sub batch_import { ); my $billtime = time; my %cust_pkg = ( pkgpart => $pkgpart ); - my %svc_acct = (); + my %svc_x = (); foreach my $field ( @fields ) { if ( $field =~ /^cust_pkg\.(pkgpart|setup|bill|susp|adjourn|expire|cancel)$/ ) { @@ -6473,7 +6577,11 @@ sub batch_import { } elsif ( $field =~ /^svc_acct\.(username|_password)$/ ) { - $svc_acct{$1} = shift @columns; + $svc_x{$1} = shift @columns; + + } elsif ( $field =~ /^svc_external\.(id|title)$/ ) { + + $svc_x{$1} = shift @columns; } else { @@ -6521,18 +6629,25 @@ sub batch_import { if ( $cust_pkg{'pkgpart'} ) { my $cust_pkg = new FS::cust_pkg ( \%cust_pkg ); - my @svc_acct = (); - if ( $svc_acct{'username'} ) { + my @svc_x = (); + my $svcdb = ''; + if ( $svc_x{'username'} ) { + $svcdb = 'svc_acct'; + } elsif ( $svc_x{'id'} || $svc_x{'title'} ) { + $svcdb = 'svc_external'; + } + if ( $svcdb ) { my $part_pkg = $cust_pkg->part_pkg; unless ( $part_pkg ) { $dbh->rollback if $oldAutoCommit; return "unknown pkgpart: ". $cust_pkg{'pkgpart'}; } - $svc_acct{svcpart} = $part_pkg->svcpart( 'svc_acct' ); - push @svc_acct, new FS::svc_acct ( \%svc_acct ) + $svc_x{svcpart} = $part_pkg->svcpart( $svcdb ); + my $class = "FS::$svcdb"; + push @svc_x, $class->new( \%svc_x ); } - $hash{$cust_pkg} = \@svc_acct; + $hash{$cust_pkg} = \@svc_x; } my $error = $cust_main->insert( \%hash, $invoicing_list ); @@ -6940,7 +7055,7 @@ sub _agent_plandata { " AND action = 'cust_bill_send_agent' ". " AND ( disabled IS NULL OR disabled != 'Y' ) ". " AND peo_agentnum.optionname = 'agentnum' ". - " AND agentnum IS NULL OR agentnum = $agentnum ". + " AND ( agentnum IS NULL OR agentnum = $agentnum ) ". " ORDER BY CASE WHEN peo_cust_bill_age.optionname != 'cust_bill_age' THEN -1