X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=d0a071cc3c6b3e0b2d2832fd03ac46840754fddd;hb=a816e075c54631250faed653d4ec7b69f727e93a;hp=15bbdc372629d24dffc94ca685d038008ab45007;hpb=7726d9797f078c5de3358a78018dca3e4ec5e966;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 15bbdc372..d0a071cc3 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -3,8 +3,9 @@ package FS::cust_main; require 5.006; use strict; #FS::cust_main:_Marketgear when they're ready to move to 2.1 -use base qw( FS::cust_main::Packages +use base qw( FS::cust_main::Packages FS::cust_main::Status FS::cust_main::Billing FS::cust_main::Billing_Realtime + FS::cust_main::Billing_Discount FS::otaker_Mixin FS::payinfo_Mixin FS::cust_main_Mixin FS::geocode_Mixin FS::Record @@ -34,6 +35,7 @@ use FS::Record qw( qsearchs qsearch dbdef regexp_sql ); use FS::Misc qw( generate_email send_email generate_ps do_print ); use FS::Msgcat qw(gettext); use FS::CurrentUser; +use FS::TicketSystem; use FS::payby; use FS::cust_pkg; use FS::cust_svc; @@ -63,7 +65,8 @@ use FS::type_pkgs; use FS::payment_gateway; use FS::agent_payment_gateway; use FS::banned_pay; -use FS::TicketSystem; +use FS::cust_main_note; +use FS::cust_attachment; # 1 is mostly method/subroutine entry and options # 2 traces progress of some operations @@ -740,7 +743,7 @@ sub get_prepay { $prepay_credit = qsearchs( 'prepay_credit', - { 'identifier' => $prepay_credit }, + { 'identifier' => $identifier }, '', 'FOR UPDATE' ); @@ -1246,6 +1249,8 @@ sub merge { } + my $name = $self->ship_name; + my $locationnum = ''; foreach my $cust_pkg ( $self->all_pkgs ) { $cust_pkg->custnum($new_custnum); @@ -1271,6 +1276,22 @@ sub merge { $dbh->rollback if $oldAutoCommit; return $error; } + + # add customer (ship) name to svc_phone.phone_name if blank + my @cust_svc = $cust_pkg->cust_svc; + foreach my $cust_svc (@cust_svc) { + my($label, $value, $svcdb) = $cust_svc->label; + next unless $svcdb eq 'svc_phone'; + my $svc_phone = $cust_svc->svc_x; + next if $svc_phone->phone_name; + $svc_phone->phone_name($name); + my $error = $svc_phone->replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + } #not considered: @@ -1631,6 +1652,7 @@ sub check { || $self->ut_alphan('geocode') || $self->ut_floatn('cdr_termination_percentage') || $self->ut_floatn('credit_limit') + || $self->ut_numbern('billday') ; #barf. need message catalogs. i18n. etc. @@ -1809,12 +1831,17 @@ sub check { && cardtype($self->payinfo) eq "Unknown"; unless ( $ignore_banned_card ) { - my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); + my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } ); if ( $ban ) { - return 'Banned credit card: banned on '. - time2str('%a %h %o at %r', $ban->_date). - ' by '. $ban->otaker. - ' (ban# '. $ban->bannum. ')'; + if ( $ban->bantype eq 'warn' ) { + #or others depending on value of $ban->reason ? + return '_duplicate_card' unless $self->override_ban_warn; + } else { + return 'Banned credit card: banned on '. + time2str('%a %h %o at %r', $ban->_date). + ' by '. $ban->otaker. + ' (ban# '. $ban->bannum. ')'; + } } } @@ -1859,8 +1886,12 @@ sub check { } elsif ( $check_payinfo && $self->payby =~ /^(CHEK|DCHK)$/ ) { my $payinfo = $self->payinfo; - $payinfo =~ s/[^\d\@]//g; - if ( $conf->exists('echeck-nonus') ) { + $payinfo =~ s/[^\d\@\.]//g; + if ( $conf->exists('cust_main-require-bank-branch') ) { + $payinfo =~ /^(\d+)\@(\d+)\.(\d+)$/ or return 'invalid echeck account@branch.bank'; + $payinfo = "$1\@$2.$3"; + } + elsif ( $conf->exists('echeck-nonus') ) { $payinfo =~ /^(\d+)\@(\d+)$/ or return 'invalid echeck account@aba'; $payinfo = "$1\@$2"; } else { @@ -1871,12 +1902,17 @@ sub check { $self->paycvv(''); unless ( $ignore_banned_card ) { - my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); + my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } ); if ( $ban ) { - return 'Banned ACH account: banned on '. - time2str('%a %h %o at %r', $ban->_date). - ' by '. $ban->otaker. - ' (ban# '. $ban->bannum. ')'; + if ( $ban->bantype eq 'warn' ) { + #or others depending on value of $ban->reason ? + return '_duplicate_ach' unless $self->override_ban_warn; + } else { + return 'Banned ACH account: banned on '. + time2str('%a %h %o at %r', $ban->_date). + ' by '. $ban->otaker. + ' (ban# '. $ban->bannum. ')'; + } } } @@ -1952,7 +1988,7 @@ sub check { ) { $self->payname( $self->first. " ". $self->getfield('last') ); } else { - $self->payname =~ /^([\w \,\.\-\'\&]+)$/ + $self->payname =~ /^([µ_0123456789aAáÁàÀâÂåÅäÄãêæÆbBcCçÇdDðÐeEéÉèÈêÊëËfFgGhHiIíÍìÌîÎïÏjJkKlLmMnNñÑoOóÓòÒôÔöÖõÕøغpPqQrRsSßtTuUúÚùÙûÛüÜvVwWxXyYýÝÿzZþÞ \,\.\-\'\&]+)$/ or return gettext('illegal_name'). " payname: ". $self->payname; $self->payname($1); } @@ -2144,7 +2180,7 @@ sub cancel { return ( "Can't (yet) ban encrypted credit cards" ) if $self->is_encrypted($self->payinfo); - my $ban = new FS::banned_pay $self->_banned_pay_hashref; + my $ban = new FS::banned_pay $self->_new_banned_pay_hashref; my $error = $ban->insert; return ( $error ) if $error; @@ -2178,11 +2214,18 @@ sub _banned_pay_hashref { { 'payby' => $payby2ban{$self->payby}, - 'payinfo' => md5_base64($self->payinfo), + 'payinfo' => $self->payinfo, #don't ever *search* on reason! #'reason' => }; } +sub _new_banned_pay_hashref { + my $self = shift; + my $hr = $self->_banned_pay_hashref; + $hr->{payinfo} = md5_base64($hr->{payinfo}); + $hr; +} + =item notes Returns all notes (see L) for this customer. @@ -2190,12 +2233,13 @@ Returns all notes (see L) for this customer. =cut sub notes { - my $self = shift; - #order by? + my($self,$orderby_classnum) = (shift,shift); + my $orderby = "_DATE DESC"; + $orderby = "CLASSNUM ASC, $orderby" if $orderby_classnum; qsearch( 'cust_main_note', { 'custnum' => $self->custnum }, '', - 'ORDER BY _DATE DESC' + "ORDER BY $orderby", ); } @@ -3346,7 +3390,7 @@ sub charge { sub charge_postal_fee { my $self = shift; - my $pkgpart = $conf->config('postal_invoice-fee_pkgpart'); + my $pkgpart = $conf->config('postal_invoice-fee_pkgpart', $self->agentnum); return '' unless ($pkgpart && grep { $_ eq 'POST' } $self->invoicing_list); my $cust_pkg = new FS::cust_pkg ( { @@ -3789,6 +3833,9 @@ Returns a status string for this customer, currently: =back +Behavior of inactive vs. cancelled edge cases can be adjusted with the +cust_main-status_module configuration option. + =cut sub status { shift->cust_status(@_); } @@ -3826,21 +3873,11 @@ Returns a hex triplet color string for this customer's status. =cut -use vars qw(%statuscolor); -tie %statuscolor, 'Tie::IxHash', - 'prospect' => '7e0079', #'000000', #black? naw, purple - 'active' => '00CC00', #green - 'ordered' => '009999', #teal? cyan? - 'suspended' => 'FF9900', #yellow - 'cancelled' => 'FF0000', #red - 'inactive' => '0000CC', #blue -; - sub statuscolor { shift->cust_statuscolor(@_); } sub cust_statuscolor { my $self = shift; - $statuscolor{$self->cust_status}; + __PACKAGE__->statuscolors->{$self->cust_status}; } =item tickets @@ -3936,8 +3973,8 @@ Class method that returns the list of possible status strings for customers =cut sub statuses { - #my $self = shift; #could be class... - keys %statuscolor; + my $self = shift; + keys %{ $self->statuscolors }; } =item cust_status_sql @@ -3981,13 +4018,14 @@ sub prospect_sql { =item ordered_sql Returns an SQL expression identifying ordered cust_main records (customers with -recurring packages not yet setup). +no active packages, but recurring packages not yet setup or one time charges +not yet billed). =cut sub ordered_sql { FS::cust_main->none_active_sql. - " AND 0 < ( $select_count_pkgs AND ". FS::cust_pkg->ordered_sql. " ) "; + " AND 0 < ( $select_count_pkgs AND ". FS::cust_pkg->not_yet_billed_sql. " ) "; } =item active_sql @@ -4046,22 +4084,7 @@ Returns an SQL expression identifying cancelled cust_main records. =cut -sub cancelled_sql { cancel_sql(@_); } -sub cancel_sql { - - my $recurring_sql = FS::cust_pkg->recurring_sql; - my $cancelled_sql = FS::cust_pkg->cancelled_sql; - - " - 0 < ( $select_count_pkgs ) - AND 0 < ( $select_count_pkgs AND $recurring_sql AND $cancelled_sql ) - AND 0 = ( $select_count_pkgs AND $recurring_sql - AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 ) - ) - "; -# AND 0 = ( $select_count_pkgs AND ". FS::cust_pkg->inactive_sql. " ) - -} +sub cancel_sql { shift->cancelled_sql(@_); } =item uncancel_sql =item uncancelled_sql