From 8333462f80da553234ef53076a932e9f417e6ad2 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 17 Apr 2008 01:39:53 +0000 Subject: [PATCH] implement line item bundling --- FS/FS/Schema.pm | 1 + FS/FS/cust_bill.pm | 8 +- FS/FS/cust_bill_pkg.pm | 19 +- FS/FS/cust_main.pm | 516 +++++++++++++++++++++++----------------------- FS/FS/cust_main_county.pm | 2 +- FS/FS/cust_pkg.pm | 6 +- FS/FS/part_pkg/flat.pm | 3 +- 7 files changed, 286 insertions(+), 269 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 2666c533a..f8dd38b8c 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -481,6 +481,7 @@ sub tables_hashref { 'columns' => [ 'billpkgnum', 'serial', '', '', '', '', 'pkgnum', 'int', '', '', '', '', + 'pkgpart_override', 'int', 'NULL', '', '', '', 'invnum', 'int', '', '', '', '', 'setup', @money_type, '', '', 'recur', @money_type, '', '', diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index f536c972c..81b874add 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -1347,7 +1347,7 @@ sub print_csv { if ( $cust_bill_pkg->pkgnum ) { ($pkg, $setup, $recur, $sdate, $edate) = ( - $cust_bill_pkg->cust_pkg->part_pkg->pkg, + $cust_bill_pkg->part_pkg->pkg, ( $cust_bill_pkg->setup != 0 ? sprintf("%.2f", $cust_bill_pkg->setup ) : '' ), @@ -1468,7 +1468,7 @@ sub realtime_bop { $cust_main->agentnum. ")"; my $agent = $agent_obj->agent; my $pkgs = join(', ', - map { $_->cust_pkg->part_pkg->pkg } + map { $_->part_pkg->pkg } grep { $_->pkgnum } $self->cust_bill_pkg ); $description = eval qq("$dtempl"); @@ -2412,7 +2412,7 @@ sub _items_sections { if ( $cust_bill_pkg->pkgnum > 0 ) { - my $desc = $cust_bill_pkg->cust_pkg->part_pkg->classname; + my $desc = $cust_bill_pkg->part_pkg->classname; $s{$desc} += $cust_bill_pkg->setup if ( $cust_bill_pkg->setup != 0 ); @@ -2479,7 +2479,7 @@ sub _items_pkg { my @cust_bill_pkg = grep { $_->pkgnum && ( defined($section) - ? $_->cust_pkg->part_pkg->classname eq $section->{'description'} + ? $_->part_pkg->classname eq $section->{'description'} : 1 ) } $self->cust_bill_pkg; diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 9fddf6bf5..86527a886 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -5,6 +5,7 @@ use vars qw( @ISA ); use FS::Record qw( qsearch qsearchs dbdef dbh ); use FS::cust_main_Mixin; use FS::cust_pkg; +use FS::part_pkg; use FS::cust_bill; use FS::cust_bill_pkg_detail; use FS::cust_bill_pay_pkg; @@ -45,6 +46,7 @@ supported: =item pkgnum - package (see L) or 0 for the special virtual sales tax package, or -1 for the virtual line item (itemdesc is used for the line) +=item pkgpart_override - optional package definition (see L) override =item setup - setup fee =item recur - recurring fee @@ -192,6 +194,21 @@ sub cust_pkg { qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } ); } +=item part_pkg + +Returns the package definition for this invoice line item. + +=cut + +sub part_pkg { + my $self = shift; + if ( $self->pkgpart_override ) { + qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart_override } ); + } else { + $self->cust_pkg->part_pkg; + } +} + =item cust_bill Returns the invoice (see L) for this invoice line item. @@ -231,7 +248,7 @@ sub desc { my $self = shift; if ( $self->pkgnum > 0 ) { - $self->cust_pkg->part_pkg->pkg; + $self->part_pkg->pkg; } else { $self->itemdesc || 'Tax'; } diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index c7d497ac9..76cea831d 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2038,8 +2038,6 @@ sub bill { my $time = $options{'time'} || time; - my $error; - #put below somehow? local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -2054,21 +2052,7 @@ sub bill { $self->select_for_update; #mutex - #create a new invoice - #(we'll remove it later if it doesn't actually need to be generated [contains - # no line items] and we're inside a transaciton so nothing else will see it) - my $cust_bill = new FS::cust_bill ( { - 'custnum' => $self->custnum, - '_date' => ( $options{'invoice_time'} || $time ), - #'charged' => $charged, - 'charged' => 0, - } ); - $error = $cust_bill->insert; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "can't create invoice for customer #". $self->custnum. ": $error"; - } - my $invnum = $cust_bill->invnum; + my @cust_bill_pkg = (); ### # find the packages which are due for billing, find out how much they are @@ -2094,202 +2078,220 @@ sub bill { unless defined($cust_pkg->bill); #my $part_pkg = $cust_pkg->part_pkg; - my @part_pkg = $cust_pkg->part_pkg->self_and_bill_linked; + my $real_pkgpart = $cust_pkg->pkgpart; my %hash = $cust_pkg->hash; my $old_cust_pkg = new FS::cust_pkg \%hash; - my @details = (); - - ### - # bill setup - ### - - my $setup = 0; - if ( ! $cust_pkg->setup && - ( - ( $conf->exists('disable_setup_suspended_pkgs') && - ! $cust_pkg->getfield('susp') - ) || ! $conf->exists('disable_setup_suspended_pkgs') - ) - || $options{'resetup'} - ) { - - warn " bill setup\n" if $DEBUG > 1; - - $setup = eval { $cust_pkg->calc_setup( $time, \@details ) }; - if ( $@ ) { - $dbh->rollback if $oldAutoCommit; - return "$@ running calc_setup for $cust_pkg\n"; - } + foreach my $part_pkg ( $cust_pkg->part_pkg->self_and_bill_linked ) { - $cust_pkg->setfield('setup', $time) unless $cust_pkg->setup; - } - - ### - # bill recurring fee - ### - - my $recur = 0; - my $sdate; - if ( $part_pkg->getfield('freq') ne '0' && - ! $cust_pkg->getfield('susp') && - ( $cust_pkg->getfield('bill') || 0 ) <= $time - ) { + $cust_pkg->pkgpart($part_pkg->pkgpart); + $cust_pkg->set($_, $hash{$_}) foreach qw( setup last_bill bill ); + + my @details = (); - # XXX should this be a package event? probably. events are called - # at collection time at the moment, though... - foreach my $part_pkg ( grep { $_->can('reset_usage') } @part_pkg ) { - warn " resetting usage counters" if $DEBUG > 1; - $part_pkg->reset_usage($cust_pkg); - } + my $lineitems = 0; - warn " bill recur\n" if $DEBUG > 1; + ### + # bill setup + ### - # XXX shared with $recur_prog - $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; + my $setup = 0; + if ( ! $cust_pkg->setup && + ( + ( $conf->exists('disable_setup_suspended_pkgs') && + ! $cust_pkg->getfield('susp') + ) || ! $conf->exists('disable_setup_suspended_pkgs') + ) + || $options{'resetup'} + ) { + + warn " bill setup\n" if $DEBUG > 1; + $lineitems++; - #over two params! lets at least switch to a hashref for the rest... - my %param = ( 'precommit_hooks' => \@precommit_hooks, ); + $setup = eval { $cust_pkg->calc_setup( $time, \@details ) }; + if ( $@ ) { + $dbh->rollback if $oldAutoCommit; + return "$@ running calc_setup for $cust_pkg\n"; + } - $recur = eval { $cust_pkg->calc_recur( \$sdate, \@details, \%param ) }; - if ( $@ ) { - $dbh->rollback if $oldAutoCommit; - return "$@ running calc_recur for $cust_pkg\n"; - } + $cust_pkg->setfield('setup', $time) + unless $cust_pkg->setup; + #do need it, but it won't get written to the db + #|| $cust_pkg->pkgpart != $real_pkgpart; - #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; - $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 { - $dbh->rollback if $oldAutoCommit; - return "unparsable frequency: ". $part_pkg->freq; } - $cust_pkg->setfield('bill', - timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year)); - } - - warn "\$setup is undefined" unless defined($setup); - warn "\$recur is undefined" unless defined($recur); - warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill); - ### - # If $cust_pkg has been modified, update it and create cust_bill_pkg records - ### - - if ( $cust_pkg->modified ) { # hmmm.. and if the options are modified? - - warn " package ". $cust_pkg->pkgnum. " modified; updating\n" - if $DEBUG >1; + ### + # bill recurring fee + ### + + my $recur = 0; + my $sdate; + if ( $part_pkg->getfield('freq') ne '0' && + ! $cust_pkg->getfield('susp') && + ( $cust_pkg->getfield('bill') || 0 ) <= $time + ) { + + # XXX should this be a package event? probably. events are called + # at collection time at the moment, though... + $part_pkg->reset_usage($cust_pkg, 'debug'=>$DEBUG) + if $part_pkg->can('reset_usage'); + #don't want to reset usage just cause we want a line item?? + #&& $part_pkg->pkgpart == $real_pkgpart; + + warn " bill recur\n" if $DEBUG > 1; + $lineitems++; + + # XXX shared with $recur_prog + $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, ); + + $recur = eval { $cust_pkg->calc_recur( \$sdate, \@details, \%param ) }; + if ( $@ ) { + $dbh->rollback if $oldAutoCommit; + return "$@ running calc_recur for $cust_pkg\n"; + } - $error=$cust_pkg->replace($old_cust_pkg, - options => { $cust_pkg->options }, - ); - if ( $error ) { #just in case - $dbh->rollback if $oldAutoCommit; - return "Error modifying pkgnum ". $cust_pkg->pkgnum. ": $error"; + + #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; + $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 { + $dbh->rollback if $oldAutoCommit; + return "unparsable frequency: ". $part_pkg->freq; + } + $cust_pkg->setfield('bill', + timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year)); + } - $setup = sprintf( "%.2f", $setup ); - $recur = sprintf( "%.2f", $recur ); - if ( $setup < 0 && ! $conf->exists('allow_negative_charges') ) { - $dbh->rollback if $oldAutoCommit; - return "negative setup $setup for pkgnum ". $cust_pkg->pkgnum; - } - if ( $recur < 0 && ! $conf->exists('allow_negative_charges') ) { - $dbh->rollback if $oldAutoCommit; - return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum; - } + warn "\$setup is undefined" unless defined($setup); + warn "\$recur is undefined" unless defined($recur); + warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill); + + ### + # If there's line items, create em cust_bill_pkg records + # If $cust_pkg has been modified, update it (if we're a real pkgpart) + ### + + if ( $lineitems ) { - if ( $setup != 0 || $recur != 0 ) { - - warn " charges (setup=$setup, recur=$recur); adding line items\n" - if $DEBUG > 1; - my $cust_bill_pkg = new FS::cust_bill_pkg ({ - 'invnum' => $invnum, - 'pkgnum' => $cust_pkg->pkgnum, - 'setup' => $setup, - 'recur' => $recur, - 'sdate' => $sdate, - 'edate' => $cust_pkg->bill, - 'details' => \@details, - }); - $error = $cust_bill_pkg->insert; - if ( $error ) { + if ( $cust_pkg->modified && $cust_pkg->pkgpart == $real_pkgpart ) { + # hmm.. and if just the options are modified in some weird price plan? + + warn " package ". $cust_pkg->pkgnum. " modified; updating\n" + if $DEBUG >1; + + my $error = $cust_pkg->replace( $old_cust_pkg, + 'options' => { $cust_pkg->options }, + ); + if ( $error ) { #just in case + $dbh->rollback if $oldAutoCommit; + return "Error modifying pkgnum ". $cust_pkg->pkgnum. ": $error"; + } + } + + $setup = sprintf( "%.2f", $setup ); + $recur = sprintf( "%.2f", $recur ); + if ( $setup < 0 && ! $conf->exists('allow_negative_charges') ) { $dbh->rollback if $oldAutoCommit; - return "can't create invoice line item for invoice #$invnum: $error"; + return "negative setup $setup for pkgnum ". $cust_pkg->pkgnum; } - $total_setup += $setup; - $total_recur += $recur; - - ### - # handle taxes - ### - - unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) { - - my @taxes = (); - my @taxoverrides = $part_pkg->part_pkg_taxoverride; - - my $prefix = - ( $conf->exists('tax-ship_address') && length($self->ship_last) ) - ? 'ship_' - : ''; - - if ( $conf->exists('enable_taxproducts') - && (scalar(@taxoverrides) || $part_pkg->taxproductnum ) - ) - { - - my @taxclassnums = (); - my $geocode = $self->geocode('cch'); - - if ( scalar( @taxoverrides ) ) { - @taxclassnums = map { $_->taxclassnum } @taxoverrides; - }elsif ( $part_pkg->taxproductnum ) { - @taxclassnums = map { $_->taxclassnum } - $part_pkg->part_pkg_taxrate('cch', $geocode); - } - - my $extra_sql = - "AND (". - join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")"; - - @taxes = qsearch({ 'table' => 'tax_rate', - 'hashref' => { 'geocode' => $geocode, }, - 'extra_sql' => $extra_sql, - }) - if scalar(@taxclassnums); - - - }else{ - - my %taxhash = map { $_ => $self->get("$prefix$_") } - qw( state county country ); - - $taxhash{'taxclass'} = $part_pkg->taxclass; - - @taxes = qsearch( 'cust_main_county', \%taxhash ); + if ( $recur < 0 && ! $conf->exists('allow_negative_charges') ) { + $dbh->rollback if $oldAutoCommit; + return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum; + } + + if ( $setup != 0 || $recur != 0 ) { + + warn " charges (setup=$setup, recur=$recur); adding line items\n" + if $DEBUG > 1; + my $cust_bill_pkg = new FS::cust_bill_pkg { + 'pkgnum' => $cust_pkg->pkgnum, + 'setup' => $setup, + 'recur' => $recur, + 'sdate' => $sdate, + 'edate' => $cust_pkg->bill, + 'details' => \@details, + }; + $cust_bill_pkg->pkgpart_override($part_pkg->pkgpart) + unless $part_pkg->pkgpart == $real_pkgpart; + push @cust_bill_pkg, $cust_bill_pkg; + + $total_setup += $setup; + $total_recur += $recur; + + ### + # handle taxes + ### + + unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) { + + my @taxes = (); + my @taxoverrides = $part_pkg->part_pkg_taxoverride; + + my $prefix = + ( $conf->exists('tax-ship_address') && length($self->ship_last) ) + ? 'ship_' + : ''; + + if ( $conf->exists('enable_taxproducts') + && (scalar(@taxoverrides) || $part_pkg->taxproductnum ) + ) + { + + my @taxclassnums = (); + my $geocode = $self->geocode('cch'); + + if ( scalar( @taxoverrides ) ) { + @taxclassnums = map { $_->taxclassnum } @taxoverrides; + }elsif ( $part_pkg->taxproductnum ) { + @taxclassnums = map { $_->taxclassnum } + $part_pkg->part_pkg_taxrate('cch', $geocode); + } + + my $extra_sql = + "AND (". + join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")"; + + @taxes = qsearch({ 'table' => 'tax_rate', + 'hashref' => { 'geocode' => $geocode, }, + 'extra_sql' => $extra_sql, + }) + if scalar(@taxclassnums); + + + }else{ + + my %taxhash = map { $_ => $self->get("$prefix$_") } + qw( state county country ); + + $taxhash{'taxclass'} = $part_pkg->taxclass; + + @taxes = qsearch( 'cust_main_county', \%taxhash ); unless ( @taxes ) { $taxhash{'taxclass'} = ''; @@ -2302,69 +2304,57 @@ sub bill { @taxes = qsearch( 'cust_main_county', \%taxhash ); } - } #if $conf->exists('enable_taxproducts') - - # maybe eliminate this entirely, along with all the 0% records - unless ( @taxes ) { - $dbh->rollback if $oldAutoCommit; - my $error; - if ( $conf->exists('enable_taxproducts') ) { - $error = - "fatal: can't find tax rate for zip/taxproduct/pkgpart ". - join('/', ( map $self->get("$prefix$_"), - qw(zip) - ), - $part_pkg->taxproduct_description, - $part_pkg->pkgpart ). "\n"; - }else{ - $error = - "fatal: can't find tax rate for state/county/country/taxclass ". - join('/', ( map $self->get("$prefix$_"), - qw(state county country) - ), - $part_pkg->taxclass ). "\n"; - } - return $error; - } + } #if $conf->exists('enable_taxproducts') - foreach my $tax ( @taxes ) { - my $taxname = ref( $tax ). ' '. $tax->taxnum; - if ( exists( $taxlisthash{ $taxname } ) ) { - push @{ $taxlisthash{ $taxname } }, $cust_bill_pkg; - }else{ - $taxlisthash{ $taxname } = [ $tax, $cust_bill_pkg ]; + # maybe eliminate this entirely, along with all the 0% records + unless ( @taxes ) { + $dbh->rollback if $oldAutoCommit; + my $error; + if ( $conf->exists('enable_taxproducts') ) { + $error = + "fatal: can't find tax rate for zip/taxproduct/pkgpart ". + join('/', ( map $self->get("$prefix$_"), + qw(zip) + ), + $part_pkg->taxproduct_description, + $part_pkg->pkgpart ). "\n"; + } else { + $error = + "fatal: can't find tax rate for state/county/country/taxclass ". + join('/', ( map $self->get("$prefix$_"), + qw(state county country) + ), + $part_pkg->taxclass ). "\n"; + } + return $error; + } + + foreach my $tax ( @taxes ) { + my $taxname = ref( $tax ). ' '. $tax->taxnum; + if ( exists( $taxlisthash{ $taxname } ) ) { + push @{ $taxlisthash{ $taxname } }, $cust_bill_pkg; + }else{ + $taxlisthash{ $taxname } = [ $tax, $cust_bill_pkg ]; + } } - } - } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP' + } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP' - } #if $setup != 0 || $recur != 0 + } #if $setup != 0 || $recur != 0 - } #if $cust_pkg->modified - - } #foreach my $cust_pkg + } #if $cust_pkg->modified - unless ( $cust_bill->cust_bill_pkg ) { - $cust_bill->delete; #don't create an invoice w/o line items + } #foreach my $part_pkg - # XXX this seems to be broken - #( DBD::Pg::st execute failed: ERROR: syntax error at or near "hcb" ) -# # get rid of our fake history too, waste of unecessary space -# my $h_cleanup_query = q{ -# DELETE FROM h_cust_bill hcb -# WHERE hcb.invnum = ? -# AND NOT EXISTS ( SELECT 1 FROM cust_bill cb where cb.invnum = hcb.invnum ) -# }; -# my $h_sth = $dbh->prepare($h_cleanup_query); -# $h_sth->execute($invnum); + } #foreach my $cust_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; return ''; } - my $charged = sprintf( "%.2f", $total_setup + $total_recur ); - foreach my $tax ( keys %taxlisthash ) { my $tax_object = shift @{ $taxlisthash{$tax} }; my $listref_or_error = $tax_object->taxline( @{ $taxlisthash{$tax} } ); @@ -2378,33 +2368,43 @@ sub bill { } foreach my $taxname ( grep { $tax{$_} > 0 } keys %tax ) { - my $tax = sprintf("%.2f", $tax{$taxname} ); - $charged = sprintf( "%.2f", $charged+$tax ); + my $tax = sprintf('%.2f', $tax{$taxname} ); + $total_setup = sprintf('%.2f', $total_setup+$tax ); - my $cust_bill_pkg = new FS::cust_bill_pkg ({ - 'invnum' => $invnum, + push @cust_bill_pkg, new FS::cust_bill_pkg { 'pkgnum' => 0, 'setup' => $tax, 'recur' => 0, 'sdate' => '', 'edate' => '', 'itemdesc' => $taxname, - }); - $error = $cust_bill_pkg->insert; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "can't create invoice line item for invoice #$invnum: $error"; - } - $total_setup += $tax; + }; } - $cust_bill->charged( sprintf( "%.2f", $total_setup + $total_recur ) ); - $error = $cust_bill->replace; + my $charged = sprintf('%.2f', $total_setup + $total_recur ); + + #create the new invoice + my $cust_bill = new FS::cust_bill ( { + 'custnum' => $self->custnum, + '_date' => ( $options{'invoice_time'} || $time ), + 'charged' => $charged, + } ); + my $error = $cust_bill->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "can't update charged for invoice #$invnum: $error"; + return "can't create invoice for customer #". $self->custnum. ": $error"; + } + + foreach my $cust_bill_pkg ( @cust_bill_pkg ) { + $cust_bill_pkg->invnum($cust_bill->invnum); + my $error = $cust_bill_pkg->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "can't create invoice line item: $error"; + } } + foreach my $hook ( @precommit_hooks ) { eval { diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm index 3a0304b2c..172eac9c6 100644 --- a/FS/FS/cust_main_county.pm +++ b/FS/FS/cust_main_county.pm @@ -184,7 +184,7 @@ sub taxline { foreach my $cust_bill_pkg (@_) { my $cust_bill = $cust_bill_pkg->cust_pkg->cust_bill; - my $part_pkg = $cust_bill_pkg->cust_pkg->part_pkg; + my $part_pkg = $cust_bill_pkg->part_pkg; my $taxable_charged = 0; $taxable_charged += $cust_bill_pkg->setup diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 834d2efb8..98b50cb67 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -763,10 +763,8 @@ Useful for billing metered services. sub last_bill { my $self = shift; - if ( $self->dbdef_table->column('last_bill') ) { - return $self->setfield('last_bill', $_[0]) if @_; - return $self->getfield('last_bill') if $self->getfield('last_bill'); - } + return $self->setfield('last_bill', $_[0]) if @_; + return $self->getfield('last_bill') if $self->getfield('last_bill'); my $cust_bill_pkg = qsearchs('cust_bill_pkg', { 'pkgnum' => $self->pkgnum, 'edate' => $self->bill, } ); $cust_bill_pkg ? $cust_bill_pkg->sdate : $self->setup || 0; diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm index e8417ff69..8cb25a623 100644 --- a/FS/FS/part_pkg/flat.pm +++ b/FS/FS/part_pkg/flat.pm @@ -155,7 +155,8 @@ sub is_prepaid { } sub reset_usage { - my($self, $cust_pkg) = @_; + my($self, $cust_pkg, %opt) = @_; + warn " resetting usage counters" if $opt{debug} > 1; my %values = map { $_, $self->option($_) } grep { $self->option($_, 'hush') } qw(seconds upbytes downbytes totalbytes); -- 2.11.0