diff options
| author | ivan <ivan> | 2008-04-17 01:39:53 +0000 | 
|---|---|---|
| committer | ivan <ivan> | 2008-04-17 01:39:53 +0000 | 
| commit | 8333462f80da553234ef53076a932e9f417e6ad2 (patch) | |
| tree | 2378a220d1f0cca147f388291616142950f8df13 | |
| parent | 9395c928f610ec9fa4389ea9994316fdcc4a2df4 (diff) | |
implement line item bundling
| -rw-r--r-- | FS/FS/Schema.pm | 1 | ||||
| -rw-r--r-- | FS/FS/cust_bill.pm | 8 | ||||
| -rw-r--r-- | FS/FS/cust_bill_pkg.pm | 19 | ||||
| -rw-r--r-- | FS/FS/cust_main.pm | 516 | ||||
| -rw-r--r-- | FS/FS/cust_main_county.pm | 2 | ||||
| -rw-r--r-- | FS/FS/cust_pkg.pm | 6 | ||||
| -rw-r--r-- | 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<FS::cust_pkg>) 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<FS::part_pkg>) 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<FS::cust_bill>) 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);  | 
