X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=551f8b3456a4847492e4e89fccac286de79a5ce7;hb=6a7573c32518c6c124c91f61dbd950ea23adaae7;hp=8e2fa19a444187467930b66bc7caeed99caac258;hpb=12ff9cacb95d18ee0398c85f9e71ce8f21940136;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 8e2fa19a4..551f8b345 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -32,6 +32,7 @@ use Digest::MD5 qw(md5_base64); use Date::Format; #use Date::Manip; use File::Temp; #qw( tempfile ); +use Email::Address; use Business::CreditCard 0.28; use FS::UID qw( getotaker dbh driver_name ); use FS::Record qw( qsearchs qsearch dbdef regexp_sql ); @@ -1853,6 +1854,7 @@ sub check { || $self->ut_floatn('credit_limit') || $self->ut_numbern('billday') || $self->ut_numbern('prorate_day') + || $self->ut_flag('force_prorate_day') || $self->ut_flag('edit_subject') || $self->ut_flag('calling_list_exempt') || $self->ut_flag('invoice_noemail') @@ -2158,6 +2160,10 @@ sub check { && ! $self->custnum && $conf->exists('cust_main-require_locale'); + return "Please select a customer class" + if ! $self->classnum + && $conf->exists('cust_main-require_classnum'); + foreach my $flag (qw( tax spool_cdr squelch_cdr archived email_csv_cdr )) { $self->$flag() =~ /^(Y?)$/ or return "Illegal $flag: ". $self->$flag(); $self->$flag($1); @@ -3566,15 +3572,17 @@ sub contact_list_email { # unlike on 4.x, we have a separate list of invoice email # destinations. # make sure they're not redundant with contact emails - my $dest = $contact->firstlast . ' <' . $contact_email->emailaddress . '>'; - $emails{ $contact_email->emailaddress } = $dest; + $emails{ $contact_email->emailaddress } = + Email::Address->new( $contact->firstlast, + $contact_email->emailaddress + )->format; } } } if ( $and_invoice ) { foreach my $email ($self->invoicing_list_emailonly) { - my $dest = $self->name_short . ' <' . $email . '>'; - $emails{ $email } ||= $dest; + $emails{ $email } ||= + Email::Address->new( $self->name_short, $email )->format; } } values %emails; @@ -5726,6 +5734,93 @@ sub _upgrade_data { #class method $class->_upgrade_otaker(%opts); + # turn on encryption as part of regular upgrade, so all new records are immediately encrypted + # existing records will be encrypted in queueable_upgrade (below) + unless ($conf->exists('encryptionpublickey') || $conf->exists('encryptionprivatekey')) { + eval "use FS::Setup"; + die $@ if $@; + FS::Setup::enable_encryption(); + } + +} + +sub queueable_upgrade { + my $class = shift; + + ### encryption gets turned on in _upgrade_data, above + + eval "use FS::upgrade_journal"; + die $@ if $@; + + # prior to 2013 (commit f16665c9) payinfo was stored in history if not encrypted, + # clear that out before encrypting/tokenizing anything else + if (!FS::upgrade_journal->is_done('clear_payinfo_history')) { + foreach my $table ('cust_main','cust_pay_pending','cust_pay','cust_pay_void','cust_refund') { + my $sql = 'UPDATE h_'.$table.' SET payinfo = NULL WHERE payinfo IS NOT NULL'; + my $sth = dbh->prepare($sql) or die dbh->errstr; + $sth->execute or die $sth->errstr; + } + FS::upgrade_journal->set_done('clear_payinfo_history'); + } + + # encrypt old records + if ($conf->exists('encryption') && !FS::upgrade_journal->is_done('encryption_check')) { + + # allow replacement of closed cust_pay/cust_refund records + local $FS::payinfo_Mixin::allow_closed_replace = 1; + + # because it looks like nothing's changing + local $FS::Record::no_update_diff = 1; + + # commit everything immediately + local $FS::UID::AutoCommit = 1; + + # encrypt what's there + foreach my $table ('cust_main','cust_pay_pending','cust_pay','cust_pay_void','cust_refund') { + my $tclass = 'FS::'.$table; + my $lastrecnum = 0; + my @recnums = (); + while (my $recnum = _upgrade_next_recnum(dbh,$table,\$lastrecnum,\@recnums)) { + my $record = $tclass->by_key($recnum); + next unless $record; # small chance it's been deleted, that's ok + next unless grep { $record->payby eq $_ } @FS::Record::encrypt_payby; + # window for possible conflict is practically nonexistant, + # but just in case... + $record = $record->select_for_update; + if (!$record->custnum && $table eq 'cust_pay_pending') { + $record->set('custnum_pending',1); + } + my $error = $record->replace; + die $error if $error; + } + } + + FS::upgrade_journal->set_done('encryption_check'); + } + +} + +# not entirely false laziness w/ Billing_Realtime::_token_check_next_recnum +# cust_payby might get deleted while this runs +# not a method! +sub _upgrade_next_recnum { + my ($dbh,$table,$lastrecnum,$recnums) = @_; + my $recnum = shift @$recnums; + return $recnum if $recnum; + my $tclass = 'FS::'.$table; + my $sql = 'SELECT '.$tclass->primary_key. + ' FROM '.$table. + ' WHERE '.$tclass->primary_key.' > '.$$lastrecnum. + ' ORDER BY '.$tclass->primary_key.' LIMIT 500';; + my $sth = $dbh->prepare($sql) or die $dbh->errstr; + $sth->execute() or die $sth->errstr; + my @recnums; + while (my $rec = $sth->fetchrow_hashref) { + push @$recnums, $rec->{$tclass->primary_key}; + } + $sth->finish(); + $$lastrecnum = $$recnums[-1]; + return shift @$recnums; } =back