X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=fa06344933fc1734ac3ae153239918a30463ea07;hb=990d37e8da0eceab234e842a67a6665e868f38c6;hp=bbe2f8237817037e956b57aad8c1d4511dbbb05e;hpb=27ca7447dd2ef3fc35767a1494d0b1c0f0c2d79b;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index bbe2f8237..fa0634493 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -1341,7 +1341,7 @@ sub delete { } foreach my $table (qw( cust_main_invoice cust_main_exemption cust_tag )) { - foreach my $record ( qsearch( 'table', { 'custnum' => $self->custnum } ) ) { + foreach my $record ( qsearch( $table, { 'custnum' => $self->custnum } ) ) { my $error = $record->delete; if ( $error ) { $dbh->rollback if $oldAutoCommit; @@ -2749,7 +2749,13 @@ sub bill { my $real_pkgpart = $cust_pkg->pkgpart; my %hash = $cust_pkg->hash; - foreach my $part_pkg ( $cust_pkg->part_pkg->self_and_bill_linked ) { + # we could implement this bit as FS::part_pkg::has_hidden, but we already + # suffer from performance issues + $options{has_hidden} = 0; + my @part_pkg = $cust_pkg->part_pkg->self_and_bill_linked; + $options{has_hidden} = 1 if ($part_pkg[1] && $part_pkg[1]->hidden); + + foreach my $part_pkg ( @part_pkg ) { $cust_pkg->set($_, $hash{$_}) foreach qw ( setup last_bill bill ); @@ -2774,6 +2780,8 @@ sub bill { } #foreach my $cust_pkg + @cust_bill_pkg = _omit_zero_value_bundles(@cust_bill_pkg); + unless ( @cust_bill_pkg ) { #don't create an invoice w/o line items #but do commit any package date cycling that happened $dbh->commit or die $dbh->errstr if $oldAutoCommit; @@ -2795,7 +2803,14 @@ sub bill { } elsif ( $postal_pkg ) { my $real_pkgpart = $postal_pkg->pkgpart; - foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) { + # we could implement this bit as FS::part_pkg::has_hidden, but we alre +ady + # suffer from performance issues + $options{has_hidden} = 0; + my @part_pkg = $postal_pkg->part_pkg->self_and_bill_linked; + $options{has_hidden} = 1 if ($part_pkg[1] && $part_pkg[1]->hidden); + + foreach my $part_pkg ( @part_pkg ) { my %postal_options = %options; delete $postal_options{cancel}; my $error = @@ -2816,6 +2831,9 @@ sub bill { } } + # it's silly to have a zero value postal_pkg, but.... + @cust_bill_pkg = _omit_zero_value_bundles(@cust_bill_pkg); + } } @@ -3037,6 +3055,27 @@ sub bill { ''; #no error } +#discard bundled packages of 0 value +sub _omit_zero_value_bundles { + + my @cust_bill_pkg = (); + my @cust_bill_pkg_bundle = (); + my $sum = 0; + + foreach my $cust_bill_pkg ( @_ ) { + if (scalar(@cust_bill_pkg_bundle) && !$cust_bill_pkg->pkgpart_override) { + push @cust_bill_pkg, @cust_bill_pkg_bundle if $sum > 0; + @cust_bill_pkg_bundle = (); + $sum = 0; + } + $sum += $cust_bill_pkg->setup + $cust_bill_pkg->recur; + push @cust_bill_pkg_bundle, $cust_bill_pkg; + } + push @cust_bill_pkg, @cust_bill_pkg_bundle if $sum > 0; + + (@cust_bill_pkg); + +} sub _make_lines { my ($self, %params) = @_; @@ -3177,7 +3216,7 @@ sub _make_lines { # If $cust_pkg has been modified, update it (if we're a real pkgpart) ### - if ( $lineitems ) { + if ( $lineitems || $options{has_hidden} ) { if ( $cust_pkg->modified && $cust_pkg->pkgpart == $real_pkgpart ) { # hmm.. and if just the options are modified in some weird price plan? @@ -3201,7 +3240,10 @@ sub _make_lines { return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum; } - if ( $setup != 0 || $recur != 0 ) { + if ( $setup != 0 || + $recur != 0 || + !$part_pkg->hidden && $options{has_hidden} ) #include some $0 lines + { warn " charges (setup=$setup, recur=$recur); adding line items\n" if $DEBUG > 1; @@ -3358,16 +3400,15 @@ sub _handle_taxes { my @display = (); my $separate = $conf->exists('separate_usage'); - my $usage_mandate = $cust_pkg->part_pkg->option('usage_mandate', 'Hush!'); - if ( $separate || $cust_bill_pkg->hidden || $usage_mandate ) { + my $temp_pkg = new FS::cust_pkg { pkgpart => $real_pkgpart }; + my $usage_mandate = $temp_pkg->part_pkg->option('usage_mandate', 'Hush!'); + my $section = $temp_pkg->part_pkg->categoryname; + if ( $separate || $section || $usage_mandate ) { - my $temp_pkg = new FS::cust_pkg { pkgpart => $real_pkgpart }; - my %hash = $cust_bill_pkg->hidden # maybe for all bill linked? - ? ( 'section' => $temp_pkg->part_pkg->categoryname ) - : (); + my %hash = ( 'section' => $section ); - my $section = $cust_pkg->part_pkg->option('usage_section', 'Hush!'); - my $summary = $cust_pkg->part_pkg->option('summarize_usage', 'Hush!'); + $section = $temp_pkg->part_pkg->option('usage_section', 'Hush!'); + my $summary = $temp_pkg->part_pkg->option('summarize_usage', 'Hush!'); if ( $separate ) { push @display, new FS::cust_bill_pkg_display { type => 'S', %hash }; push @display, new FS::cust_bill_pkg_display { type => 'R', %hash }; @@ -3389,8 +3430,10 @@ sub _handle_taxes { $hash{post_total} = 'Y'; } - $hash{section} = $section if ($separate || $usage_mandate); - push @display, new FS::cust_bill_pkg_display { type => 'U', %hash }; + if ($separate || $usage_mandate) { + $hash{section} = $section if ($separate || $usage_mandate); + push @display, new FS::cust_bill_pkg_display { type => 'U', %hash }; + } } $cust_bill_pkg->set('display', \@display); @@ -4617,28 +4660,42 @@ sub realtime_bop { && ! grep { $transaction->error_message =~ /$_/ } $conf->config('emaildecline-exclude') ) { - my @templ = $conf->config('declinetemplate'); - my $template = new Text::Template ( - TYPE => 'ARRAY', - SOURCE => [ map "$_\n", @templ ], - ) or return "($perror) can't create template: $Text::Template::ERROR"; - $template->compile() - or return "($perror) can't compile template: $Text::Template::ERROR"; - my $templ_hash = { - 'company_name' => - scalar( $conf->config('company_name', $self->agentnum ) ), - 'company_address' => - join("\n", $conf->config('company_address', $self->agentnum ) ), - 'error' => $transaction->error_message, - }; + # Send a decline alert to the customer. + my $msgnum = $conf->config('decline_msgnum', $self->agentnum); + my $error = ''; + if ( $msgnum ) { + # include the raw error message in the transaction state + $cust_pay_pending->setfield('error', $transaction->error_message); + my $msg_template = qsearchs('msg_template', { msgnum => $msgnum }); + $error = $msg_template->send( 'cust_main' => $self, + 'object' => $cust_pay_pending ); + } + else { #!$msgnum + + my @templ = $conf->config('declinetemplate'); + my $template = new Text::Template ( + TYPE => 'ARRAY', + SOURCE => [ map "$_\n", @templ ], + ) or return "($perror) can't create template: $Text::Template::ERROR"; + $template->compile() + or return "($perror) can't compile template: $Text::Template::ERROR"; + + my $templ_hash = { + 'company_name' => + scalar( $conf->config('company_name', $self->agentnum ) ), + 'company_address' => + join("\n", $conf->config('company_address', $self->agentnum ) ), + 'error' => $transaction->error_message, + }; - my $error = send_email( - 'from' => $conf->config('invoice_from', $self->agentnum ), - 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ], - 'subject' => 'Your payment could not be processed', - 'body' => [ $template->fill_in(HASH => $templ_hash) ], - ); + my $error = send_email( + 'from' => $conf->config('invoice_from', $self->agentnum ), + 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ], + 'subject' => 'Your payment could not be processed', + 'body' => [ $template->fill_in(HASH => $templ_hash) ], + ); + } $perror .= " (also received error sending decline notification: $error)" if $error; @@ -5555,8 +5612,7 @@ sub _new_realtime_bop { #false laziness w/misc/process/payment.cgi - check both to make sure working # correctly - if ( defined $self->dbdef_table->column('paycvv') - && length($self->paycvv) + if ( length($self->paycvv) && ! grep { $_ eq cardtype($options{payinfo}) } $conf->config('cvv-save') ) { my $error = $self->remove_cvv; @@ -6985,7 +7041,7 @@ sub balance_date_range { my $self = shift; my $sql = 'SELECT SUM('. $self->balance_date_sql(@_). ') FROM cust_main WHERE custnum='. $self->custnum; - sprintf( "%.2f", $self->scalar_sql($sql) ); + sprintf( '%.2f', $self->scalar_sql($sql) ); } =item balance_pkgnum PKGNUM @@ -8265,6 +8321,7 @@ Returns an SQL expression identifying un-cancelled cust_main records. sub uncancelled_sql { uncancel_sql(@_); } sub uncancel_sql { " + ( 0 < ( $select_count_pkgs AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 @@ -9396,6 +9453,9 @@ sub batch_charge { =item notify CUSTOMER_OBJECT TEMPLATE_NAME OPTIONS +Deprecated. Use event notification and message templates +(L) instead. + Sends a templated email notification to the customer (see L). OPTIONS is a hash and may include @@ -9405,6 +9465,8 @@ I - the email sender (default is invoice_from) I - comma-separated scalar or arrayref of recipients (default is invoicing_list) +I - blind-copy recipient address (default is none) + I - The subject line of the sent email notification (default is "Notice from company_name") @@ -9481,6 +9543,7 @@ sub notify { send_email(from => $from, to => $to, + bcc => $options{bcc}, subject => $subject, body => $notify_template->fill_in( PACKAGE => 'FS::notify_template::_template' ), @@ -9509,6 +9572,7 @@ I<$returnaddress> - the return address defaults to invoice_latexreturnaddress or =cut +# a lot like cust_bill::print_latex sub generate_letter { my ($self, $template, %options) = @_; @@ -9559,8 +9623,13 @@ sub generate_letter { $letter_data{returnaddress} = $retadd; } elsif ( grep /\S/, $conf->config('company_address', $self->agentnum) ) { $letter_data{returnaddress} = - join( '\\*'."\n", map s/( {2,})/'~' x length($1)/eg, - $conf->config('company_address', $self->agentnum) + join( "\n", map { s/( {2,})/'~' x length($1)/eg; + s/$/\\\\\*/; + $_; + } + ( $conf->config('company_name', $self->agentnum), + $conf->config('company_address', $self->agentnum), + ) ); } else { $letter_data{returnaddress} = '~'; @@ -9572,6 +9641,17 @@ sub generate_letter { $letter_data{company_name} = $conf->config('company_name', $self->agentnum); my $dir = $FS::UID::conf_dir."/cache.". $FS::UID::datasrc; + + my $lh = new File::Temp( TEMPLATE => 'letter.'. $self->custnum. '.XXXXXXXX', + DIR => $dir, + SUFFIX => '.eps', + UNLINK => 0, + ) or die "can't open temp file: $!\n"; + print $lh $conf->config_binary('logo.eps', $self->agentnum) + or die "can't write temp file: $!\n"; + close $lh; + $letter_data{'logo_file'} = $lh->filename; + my $fh = new File::Temp( TEMPLATE => 'letter.'. $self->custnum. '.XXXXXXXX', DIR => $dir, SUFFIX => '.tex', @@ -9581,7 +9661,8 @@ sub generate_letter { $letter_template->fill_in( OUTPUT => $fh, HASH => \%letter_data ); close $fh; $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename; - return $1; + return ($1, $letter_data{'logo_file'}); + } =item print_ps TEMPLATE @@ -9592,8 +9673,12 @@ Returns an postscript letter filled in from TEMPLATE, as a scalar. sub print_ps { my $self = shift; - my $file = $self->generate_letter(@_); - FS::Misc::generate_ps($file); + my($file, $lfile) = $self->generate_letter(@_); + my $ps = FS::Misc::generate_ps($file); + unlink($file.'.tex'); + unlink($lfile); + + $ps; } =item print TEMPLATE