X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=af388d3a6eb233b8759145ccdc9e5719729c8f1f;hp=6581578f5df11b14cbc9707d3cefb2c7183bba22;hb=b3c2e70e63eb8f7ab2543a4d400f72bd5044a9f9;hpb=3abef78632d45098064258023dc487e8317d8124 diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 6581578f5..af388d3a6 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -13,6 +13,7 @@ use String::ShellQuote; use HTML::Entities; use Locale::Country; use Storable qw( freeze thaw ); +use GD::Barcode; use FS::UID qw( datasrc ); use FS::Misc qw( send_email send_fax generate_ps generate_pdf do_print ); use FS::Record qw( qsearch qsearchs dbh ); @@ -37,6 +38,7 @@ use FS::part_bill_event; use FS::payby; use FS::bill_batch; use FS::cust_bill_batch; +use Cwd; @ISA = qw( FS::cust_main_Mixin FS::Record ); @@ -286,11 +288,40 @@ sub replace_check { #return "Can't change _date!" unless $old->_date eq $new->_date; return "Can't change _date" unless $old->_date == $new->_date; return "Can't change charged" unless $old->charged == $new->charged - || $old->charged == 0; + || $old->charged == 0 + || $new->{'Hash'}{'cc_surcharge_replace_hack'}; ''; } + +=item add_cc_surcharge + +Giant hack + +=cut + +sub add_cc_surcharge { + my ($self, $pkgnum, $amount) = (shift, shift, shift); + + my $error; + my $cust_bill_pkg = new FS::cust_bill_pkg({ + 'invnum' => $self->invnum, + 'pkgnum' => $pkgnum, + 'setup' => $amount, + }); + $error = $cust_bill_pkg->insert; + return $error if $error; + + $self->{'Hash'}{'cc_surcharge_replace_hack'} = 1; + $self->charged($self->charged+$amount); + $error = $self->replace; + return $error if $error; + + $self->apply_payments_and_credits; +} + + =item check Checks all fields to make sure this is a valid invoice. If there is an error, @@ -1241,8 +1272,14 @@ sub email { my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list; - #better to notify this person than silence - @invoicing_list = ($invoice_from) unless @invoicing_list; + if ( ! @invoicing_list ) { #no recipients + if ( $conf->exists('cust_bill-no_recipients-error') ) { + die 'No recipients for customer #'. $self->custnum; + } else { + #default: better to notify this person than silence + @invoicing_list = ($invoice_from); + } + } my $subject = $self->email_subject($template); @@ -1952,7 +1989,8 @@ sub realtime_lec { } sub realtime_bop { - my( $self, $method ) = @_; + my( $self, $method ) = (shift,shift); + my %opt = @_; my $cust_main = $self->cust_main; my $balance = $cust_main->balance; @@ -1981,6 +2019,7 @@ sub realtime_bop { #this didn't do what we want, it just calls apply_payments_and_credits # 'apply' => 1, 'apply_to_invoice' => 1, + %opt, #what we want: #this changes application behavior: auto payments #triggered against a specific invoice are now applied @@ -2115,6 +2154,40 @@ sub print_latex { close $lh; $params{'logo_file'} = $lh->filename; + if($conf->exists('invoice-barcode')){ + my $gdbar = new GD::Barcode('Code39',$self->invnum); + die "can't create barcode: " . $GD::Barcode::errStr unless $gdbar; + my $gd = $gdbar->plot(Height => 20); + my $bh = new File::Temp( TEMPLATE => 'barcode.'. $self->invnum. '.XXXXXXXX', + DIR => $dir, + SUFFIX => '.png', + UNLINK => 0, + ) or die "can't open temp file: $!\n"; + print $bh $gd->png or die "cannot write barcode to file: $!\n"; + + my $png_file = $bh->filename; + close $bh; + + my $eps_file = $png_file; + $eps_file =~ s/\.png$/.eps/g; + $png_file =~ /(barcode.*png)/; + $png_file = $1; + $eps_file =~ /(barcode.*eps)/; + $eps_file = $1; + + my $curr_dir = cwd(); + chdir($dir); + # after painfuly long experimentation, it was determined that sam2p won't + # accept : and other chars in the path, no matter how hard I tried to + # escape them, hence the chdir (and chdir back, just to be safe) + system('sam2p', $png_file, 'EPS:', $eps_file ) == 0 + or die "sam2p failed: $!\n"; + unlink($png_file); + chdir($curr_dir); + + $params{'barcode_file'} = $eps_file; + } + my @filled_in = $self->print_generic( %params ); my $fh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX', @@ -2126,7 +2199,7 @@ sub print_latex { close $fh; $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename; - return ($1, $params{'logo_file'}); + return ($1, $params{'logo_file'}, $params{'barcode_file'}); } @@ -2180,6 +2253,9 @@ sub print_generic { 'template' => [ '{', '}' ], ); + warn "$me print_generic creating template\n" + if $DEBUG > 1; + #create the template my $template = $params{template} ? $params{template} : $self->_agent_template; my $templatefile = "invoice_$format"; @@ -2197,12 +2273,18 @@ sub print_generic { @invoice_template = _translate_old_latex_format(@invoice_template); } + warn "$me print_generic creating T:T object\n" + if $DEBUG > 1; + my $text_template = new Text::Template( TYPE => 'ARRAY', SOURCE => \@invoice_template, DELIMITERS => $delimiters{$format}, ); + warn "$me print_generic compiling T:T object\n" + if $DEBUG > 1; + $text_template->compile() or die "Can't compile $templatefile: $Text::Template::ERROR\n"; @@ -2316,6 +2398,8 @@ sub print_generic { ); my $embolden_function = $embolden_functions{$format}; + warn "$me generating template variables\n" + if $DEBUG > 1; # generate template variables my $returnaddress; @@ -2369,6 +2453,9 @@ sub print_generic { } + warn "$me generating invoice data\n" + if $DEBUG > 1; + my $agentnum = $self->cust_main->agentnum; my %invoice_data = ( @@ -2475,6 +2562,8 @@ sub print_generic { $invoice_data{'logo_file'} = $params{'logo_file'} if $params{'logo_file'}; + $invoice_data{'barcode_file'} = $params{'barcode_file'} + if $params{'barcode_file'}; my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance # my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits @@ -2491,7 +2580,9 @@ sub print_generic { } $invoice_data{'summarypage'} = $summarypage; - #do variable substitution in notes, footer, smallfooter + warn "$me substituting variables in notes, footer, smallfooter\n" + if $DEBUG > 1; + foreach my $include (qw( notes footer smallfooter coupon )) { my $inc_file = $conf->key_orbase("invoice_${format}$include", $template); @@ -2562,6 +2653,9 @@ sub print_generic { $invoice_data{'buf'} = \@buf; $invoice_data{'sections'} = \@sections; + warn "$me generating sections\n" + if $DEBUG > 1; + my $previous_section = { 'description' => 'Previous Charges', 'subtotal' => $other_money_char. sprintf('%.2f', $pr_total), @@ -2632,6 +2726,9 @@ sub print_generic { ) { + warn "$me adding previous balances\n" + if $DEBUG > 1; + foreach my $line_item ( $self->_items_previous ) { my $detail = { @@ -2666,6 +2763,9 @@ sub print_generic { } if ( $conf->exists('svc_phone-did-summary') ) { + warn "$me adding DID summary\n" + if $DEBUG > 1; + my ($didsummary,$minutes) = $self->_did_summary; my $didsummary_desc = 'DID Activity Summary (Past 30 days)'; push @detail_items, @@ -2677,6 +2777,9 @@ sub print_generic { foreach my $section (@sections, @$late_sections) { + warn "$me adding section \n". Dumper($section) + if $DEBUG > 1; + # begin some normalization $section->{'subtotal'} = $section->{'amount'} if $multisection @@ -2702,6 +2805,9 @@ sub print_generic { ); } + warn "$me setting options\n" + if $DEBUG > 1; + my $multilocation = scalar($cust_main->cust_location); #too expensive? my %options = (); $options{'section'} = $section if $multisection; @@ -2715,7 +2821,14 @@ sub print_generic { $options{'multilocation'} = $multilocation; $options{'multisection'} = $multisection; + warn "$me searching for line items\n" + if $DEBUG > 1; + foreach my $line_item ( $self->_items_pkg(%options) ) { + + warn "$me adding line item $line_item\n" + if $DEBUG > 1; + my $detail = { ext_description => [], }; @@ -2761,6 +2874,9 @@ sub print_generic { unshift @sections, $previous_section if $pr_total; } + warn "$me adding taxes\n" + if $DEBUG > 1; + foreach my $tax ( $self->_items_tax ) { $taxtotal += $tax->{'amount'}; @@ -3094,9 +3210,10 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub print_ps { my $self = shift; - my ($file, $lfile) = $self->print_latex(@_); + my ($file, $logofile, $barcodefile) = $self->print_latex(@_); my $ps = generate_ps($file); - unlink($lfile); + unlink($logofile); + unlink($barcodefile); $ps; } @@ -3122,9 +3239,10 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub print_pdf { my $self = shift; - my ($file, $lfile) = $self->print_latex(@_); + my ($file, $logofile, $barcodefile) = $self->print_latex(@_); my $pdf = generate_pdf($file); - unlink($lfile); + unlink($logofile); + unlink($barcodefile); $pdf; } @@ -4074,9 +4192,21 @@ sub _items_previous { sub _items_pkg { my $self = shift; my %options = @_; + + warn "$me _items_pkg searching for all package line items\n" + if $DEBUG > 1; + my @cust_bill_pkg = grep { $_->pkgnum } $self->cust_bill_pkg; + + warn "$me _items_pkg filtering line items\n" + if $DEBUG > 1; my @items = $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_); + if ($options{section} && $options{section}->{condensed}) { + + warn "$me _items_pkg condensing section\n" + if $DEBUG > 1; + my %itemshash = (); local $Storable::canonical = 1; foreach ( @items ) { @@ -4096,6 +4226,10 @@ sub _items_pkg { } keys %itemshash; } + + warn "$me _items_pkg returning ". scalar(@items). " items\n" + if $DEBUG > 1; + @items; } @@ -4116,7 +4250,7 @@ sub _items_tax { sub _items_cust_bill_pkg { my $self = shift; - my $cust_bill_pkg = shift; + my $cust_bill_pkgs = shift; my %opt = @_; my $format = $opt{format} || ''; @@ -4131,11 +4265,14 @@ sub _items_cust_bill_pkg { my @b = (); my ($s, $r, $u) = ( undef, undef, undef ); - foreach my $cust_bill_pkg ( @$cust_bill_pkg ) + foreach my $cust_bill_pkg ( @$cust_bill_pkgs ) { + warn "$me _items_cust_bill_pkg considering cust_bill_pkg $cust_bill_pkg\n" + if $DEBUG > 1; + $discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount - && $conf->exists('discount-show-always')); + && $conf->exists('discount-show-always')); foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { if ( $_ && !$cust_bill_pkg->hidden ) { @@ -4158,6 +4295,9 @@ sub _items_cust_bill_pkg { ) { + warn "$me _items_cust_bill_pkg considering display item $display\n" + if $DEBUG > 1; + my $type = $display->type; my $desc = $cust_bill_pkg->desc; @@ -4171,10 +4311,16 @@ sub _items_cust_bill_pkg { if ( $cust_bill_pkg->pkgnum > 0 ) { + warn "$me _items_cust_bill_pkg cust_bill_pkg is non-tax\n" + if $DEBUG > 1; + my $cust_pkg = $cust_bill_pkg->cust_pkg; if ( $cust_bill_pkg->setup != 0 && (!$type || $type eq 'S') ) { + warn "$me _items_cust_bill_pkg adding setup\n" + if $DEBUG > 1; + my $description = $desc; $description .= ' Setup' if $cust_bill_pkg->recur != 0; @@ -4223,14 +4369,17 @@ sub _items_cust_bill_pkg { ) { + warn "$me _items_cust_bill_pkg adding recur/usage\n" + if $DEBUG > 1; + my $is_summary = $display->summary; my $description = ($is_summary && $type && $type eq 'U') ? "Usage charges" : $desc; - unless ( $conf->exists('disable_line_item_date_ranges') ) { - $description .= " (" . time2str($date_format, $cust_bill_pkg->sdate). - " - ". time2str($date_format, $cust_bill_pkg->edate). ")"; - } + $description .= " (" . time2str($date_format, $cust_bill_pkg->sdate). + " - ". time2str($date_format, $cust_bill_pkg->edate). + ")" + unless $conf->exists('disable_line_item_date_ranges'); my @d = (); @@ -4247,12 +4396,18 @@ sub _items_cust_bill_pkg { || $is_summary && $type && $type eq 'U' ) { + warn "$me _items_cust_bill_pkg adding service details\n" + if $DEBUG > 1; + push @d, map &{$escape_function}($_), $cust_pkg->h_labels_short(@dates, 'I') #$cust_bill_pkg->edate, #$cust_bill_pkg->sdate) unless $cust_bill_pkg->pkgpart_override; #don't redisplay services + warn "$me _items_cust_bill_pkg done adding service details\n" + if $DEBUG > 1; + if ( $multilocation ) { my $loc = $cust_pkg->location_label; $loc = substr($loc, 0, 50). '...' @@ -4262,8 +4417,14 @@ sub _items_cust_bill_pkg { } + warn "$me _items_cust_bill_pkg adding details\n" + if $DEBUG > 1; + push @d, $cust_bill_pkg->details(%details_opt) unless ($is_summary || $type && $type eq 'R'); + + warn "$me _items_cust_bill_pkg calculating amount\n" + if $DEBUG > 1; my $amount = 0; if (!$type) { @@ -4276,6 +4437,9 @@ sub _items_cust_bill_pkg { if ( !$type || $type eq 'R' ) { + warn "$me _items_cust_bill_pkg adding recur\n" + if $DEBUG > 1; + if ( $cust_bill_pkg->hidden ) { $r->{amount} += $amount; $r->{unit_amount} += $cust_bill_pkg->unitrecur; @@ -4294,6 +4458,9 @@ sub _items_cust_bill_pkg { } else { # $type eq 'U' + warn "$me _items_cust_bill_pkg adding usage\n" + if $DEBUG > 1; + if ( $cust_bill_pkg->hidden ) { $u->{amount} += $amount; $u->{unit_amount} += $cust_bill_pkg->unitrecur; @@ -4316,6 +4483,9 @@ sub _items_cust_bill_pkg { } else { #pkgnum tax or one-shot line item (??) + warn "$me _items_cust_bill_pkg cust_bill_pkg is tax\n" + if $DEBUG > 1; + if ( $cust_bill_pkg->setup != 0 ) { push @b, { 'description' => $desc, @@ -4337,13 +4507,16 @@ sub _items_cust_bill_pkg { } + warn "$me _items_cust_bill_pkg done considering cust_bill_pkgs\n" + if $DEBUG > 1; + foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { if ( $_ ) { $_->{amount} = sprintf( "%.2f", $_->{amount} ), $_->{amount} =~ s/^\-0\.00$/0.00/; $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ), push @b, { %$_ } - unless ( $_->{amount} == 0 && !$discount_show_always ); + unless ( $_->{amount} == 0 && !$discount_show_always ); } }