X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill_pkg.pm;h=de76b0a29206a094eb88ad3babb764ea80bab778;hb=5865a3fa94a6e7ed1183cff72de0c5948808001d;hp=2aa2a6fabe150b2afedefab28faffd60726a5d6c;hpb=428a33ad6d0e01621717840d2db4861f4bee6c21;p=freeside.git diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 2aa2a6fab..de76b0a29 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -26,6 +26,7 @@ use FS::cust_bill_pkg_discount_void; use FS::cust_bill_pkg_tax_location_void; use FS::cust_bill_pkg_tax_rate_location_void; use FS::cust_tax_exempt_pkg_void; +use FS::cust_bill_pkg_fee_void; use FS::part_fee; use FS::Cursor; @@ -299,13 +300,12 @@ sub insert { } # foreach my $link } - my $cust_event_fee = $self->get('cust_event_fee'); - if ( $cust_event_fee ) { - $cust_event_fee->set('billpkgnum' => $self->billpkgnum); - $error = $cust_event_fee->replace; + if ( my $fee_origin = $self->get('fee_origin') ) { + $fee_origin->set('billpkgnum' => $self->billpkgnum); + $error = $fee_origin->replace; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "error updating cust_event_fee: $error"; + return "error updating fee origin record: $error"; } } @@ -334,6 +334,7 @@ line item to the FS::cust_bill_pkg_void table (and related tables). sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + my $reprocess_cdrs = scalar(@_) ? shift : ''; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -363,7 +364,11 @@ sub void { cust_bill_pkg_tax_location cust_bill_pkg_tax_rate_location cust_tax_exempt_pkg + cust_bill_pkg_fee )) { + my %delete_args = (); + $delete_args{'reprocess_cdrs'} = $reprocess_cdrs + if $table eq 'cust_bill_pkg_detail'; foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) { @@ -371,7 +376,7 @@ sub void { my $void = $vclass->new( { map { $_ => $linked->get($_) } $linked->fields }); - my $error = $void->insert || $linked->delete; + my $error = $void->insert || $linked->delete(%delete_args); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -422,6 +427,7 @@ sub delete { cust_tax_exempt_pkg cust_bill_pay_pkg cust_credit_bill_pkg + cust_bill_pkg_fee )) { foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) { @@ -445,7 +451,16 @@ sub delete { } } - my $error = $self->SUPER::delete(@_); + #fix the invoice amount + + my $cust_bill = $self->cust_bill; + $cust_bill->charged( $cust_bill->charged - $self->setup - $self->recur ); + + #not adding a cc surcharge, but this override lets us modify charged + $cust_bill->{'Hash'}{'cc_surcharge_replace_hack'} = 1; + + my $error = $cust_bill->replace + || $self->SUPER::delete(@_); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -821,29 +836,51 @@ quantity. sub _item_discount { my $self = shift; + + my $d; # this will be returned. + my @pkg_discounts = $self->pkg_discount; - return if @pkg_discounts == 0; - # special case: if there are old "discount details" on this line item, don't - # show discount line items - if ( FS::cust_bill_pkg_detail->count("detail LIKE 'Includes discount%' AND billpkgnum = ?", $self->billpkgnum || 0) > 0 ) { - return; - } - - my @ext; - my $d = { - _is_discount => 1, - description => $self->mt('Discount'), - amount => 0, - ext_description => \@ext, - # maybe should show quantity/unit discount? - }; - foreach my $pkg_discount (@pkg_discounts) { - push @ext, $pkg_discount->description; - $d->{amount} -= $pkg_discount->amount; - } - $d->{amount} *= $self->quantity || 1; - - return $d; + if (@pkg_discounts) { + # special case: if there are old "discount details" on this line item, + # don't show discount line items + if ( FS::cust_bill_pkg_detail->count("detail LIKE 'Includes discount%' AND billpkgnum = ?", $self->billpkgnum || 0) > 0 ) { + return; + } + + my @ext; + $d = { + _is_discount => 1, + description => $self->mt('Discount'), + amount => 0, + ext_description => \@ext, + pkgpart => $self->pkgpart, + feepart => $self->feepart, + # maybe should show quantity/unit discount? + }; + foreach my $pkg_discount (@pkg_discounts) { + push @ext, $pkg_discount->description; + $d->{'amount'} -= $pkg_discount->amount; + } + } + + # show introductory rate as a pseudo-discount + if (!$d) { # this will conflict with showing real discounts + my $part_pkg = $self->part_pkg; + if ( $part_pkg and $part_pkg->option('show_as_discount',1) ) { + my $cust_pkg = $self->cust_pkg; + my $intro_end = $part_pkg->intro_end($cust_pkg); + my $_date = $self->cust_bill->_date; + if ( $intro_end > $_date ) { + $d = $part_pkg->item_discount($cust_pkg); + } + } + } + + if ( $d ) { + $d->{amount} *= $self->quantity || 1; + } + + $d; } =item set_display OPTION => VALUE ... @@ -1014,10 +1051,12 @@ sub usage { my $sql = 'SELECT SUM(COALESCE(amount,0)) FROM cust_bill_pkg_detail '. ' WHERE billpkgnum = '. $self->billpkgnum; - if ($classnum =~ /^(\d+)$/) { - $sql .= " AND classnum = $1"; - } elsif (defined($classnum) and $classnum eq '') { - $sql .= " AND classnum IS NULL"; + if (defined $classnum) { + if ($classnum =~ /^(\d+)$/) { + $sql .= " AND classnum = $1"; + } elsif (defined($classnum) and $classnum eq '') { + $sql .= " AND classnum IS NULL"; + } } my $sth = dbh->prepare($sql) or die dbh->errstr; @@ -1096,17 +1135,34 @@ sub cust_bill_pkg_tax_Xlocation { =item recur_show_zero -=cut +Whether to show a zero recurring amount. This is true if the package or its +definition has the recur_show_zero flag, and the recurring fee is actually +zero for this period. -sub recur_show_zero { shift->_X_show_zero('recur'); } -sub setup_show_zero { shift->_X_show_zero('setup'); } +=cut -sub _X_show_zero { +sub recur_show_zero { my( $self, $what ) = @_; - return 0 unless $self->$what() == 0 && $self->pkgnum; + return 0 unless $self->get('recur') == 0 && $self->pkgnum; + + $self->cust_pkg->_X_show_zero('recur'); +} + +=item setup_show_zero + +Whether to show a zero setup charge. This requires the package or its +definition to have the setup_show_zero flag, but it also returns false if +the package's setup date is before this line item's start date. - $self->cust_pkg->_X_show_zero($what); +=cut + +sub setup_show_zero { + my $self = shift; + return 0 unless $self->get('setup') == 0 && $self->pkgnum; + my $cust_pkg = $self->cust_pkg; + return 0 if ( $self->sdate || 0 ) > ( $cust_pkg->setup || 0 ); + return $cust_pkg->_X_show_zero('setup'); } =item credited [ BEFORE, AFTER, OPTIONS ] @@ -1151,35 +1207,6 @@ sub tax_location { } } -=item part_X - -Returns the L or L object that defines this -charge. If called on a tax line, returns nothing. - -=cut - -sub part_X { - my $self = shift; - if ( $self->pkgpart_override ) { - return FS::part_pkg->by_key($self->pkgpart_override); - } elsif ( $self->pkgnum ) { - return $self->cust_pkg->part_pkg; - } elsif ( $self->feepart ) { - return $self->part_fee; - } else { - return; - } -} - -# stubs - -sub part_fee { - my $self = shift; - $self->feepart - ? FS::part_fee->by_key($self->feepart) - : undef; -} - =back =head1 CLASS METHODS @@ -1786,6 +1813,70 @@ sub upgrade_tax_location { ''; } +sub _pkg_tax_list { + # Return an array of hashrefs for each cust_bill_pkg_tax_location + # applied to this bill for this cust_bill_pkg.pkgnum. + # + # ! Important Note: + # In some situations, this list will contain more tax records than the + # ones directly related to $self->billpkgnum. The returned list contains + # all records, for this bill, charged against this billpkgnum's pkgnum. + # + # One must keep this in mind when using data returned by this method. + # + # An unaddressed deficiency in the cust_bill_pkg_tax_location model makes + # this necessary: When a linked-hidden package generates a tax/fee as a row + # in cust_bill_pkg_tax_location, there is not enough information to surmise + # with specificity which billpkgnum row represents the direct parent of the + # the linked-hidden package's tax row. The closest we can get to this + # backwards reassociation is to use the pkgnum. Therefore, when multiple + # billpkgnum's appear with the same pkgnum, this method is going to return + # the tax records for ALL of those billpkgnum's, not just $self->billpkgnum. + # + # This could be addressed with an update to the model, and to the billing + # routine that generates rows into cust_bill_pkg_tax_location. Perhaps a + # column, link_billpkgnum or parent_billpkgnum, recording the link. I'm not + # doing that now, because there would be no possible repair of data stored + # historically prior to such a fix. I need _pkg_tax_list() to not be + # broken for already-generated bills. + # + # Any code you write relying on _pkg_tax_list() MUST be aware of, and + # account for, the possible return of duplicated tax records returned + # when method is called on multiple cust_bill_pkg_tax_location rows. + # Duplicates can be identified by billpkgtaxlocationnum column. + + my $self = shift; + return unless $self->pkgnum; + + map +{ + billpkgtaxlocationnum => $_->billpkgtaxlocationnum, + billpkgnum => $_->billpkgnum, + taxnum => $_->taxnum, + amount => $_->amount, + taxname => $_->taxname, + }, + qsearch({ + table => 'cust_bill_pkg_tax_location', + addl_from => ' + LEFT JOIN cust_bill_pkg + ON cust_bill_pkg.billpkgnum + = cust_bill_pkg_tax_location.taxable_billpkgnum + ', + select => join( ', ', (qw| + cust_bill_pkg.billpkgnum + cust_bill_pkg_tax_location.billpkgtaxlocationnum + cust_bill_pkg_tax_location.taxnum + cust_bill_pkg_tax_location.amount + |)), + extra_sql => + ' WHERE '. + ' cust_bill_pkg.invnum = ' . dbh->quote( $self->invnum ) . + ' AND '. + ' cust_bill_pkg_tax_location.pkgnum = ' . dbh->quote( $self->pkgnum ), + }); + +} + sub _upgrade_data { # Create a queue job to run upgrade_tax_location from January 1, 2012 to # the present date. @@ -1844,4 +1935,3 @@ from the base documentation. =cut 1; -