X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=47f71c45890751c7eca158fa28dba437623d322c;hp=6546bfa95b471f59c6d81bc8ed92e836973316e1;hb=a5bfed744069d69a1fe07eca1a64a2b22692cc22;hpb=cace897d9c5fbe1f80277b0cb14e85c9d2272cf1 diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 6546bfa95..47f71c458 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -11,6 +11,7 @@ use Fcntl qw(:flock); #for spool_csv use Cwd; use List::Util qw(min max sum); use Date::Format; +use DateTime; use File::Temp 0.14; use HTML::Entities; use Storable qw( freeze thaw ); @@ -37,6 +38,8 @@ use FS::cust_bill_pay_pkg; use FS::cust_credit_bill_pkg; use FS::discount_plan; use FS::cust_bill_void; +use FS::reason; +use FS::reason_type; use FS::L10N; $DEBUG = 0; @@ -103,13 +106,13 @@ Deprecated fields =over 4 =item billing_balance - the customer's balance immediately before generating -this invoice. DEPRECATED. Use the L method +this invoice. DEPRECATED. Use the L method to determine the customer's balance at a specific time. =item previous_balance - the customer's balance immediately after generating the invoice before this one. DEPRECATED. -=item printed - formerly used to track the number of times an invoice had +=item printed - formerly used to track the number of times an invoice had been printed; no longer used. =back @@ -145,15 +148,6 @@ Invoices are normally created by calling the bill method of a customer object sub table { 'cust_bill'; } sub template_conf { 'invoice_'; } -sub has_sections { - my $self = shift; - my $agentnum = $self->cust_main->agentnum; - my $tc = $self->template_conf; - - $self->conf->exists($tc.'sections', $agentnum) || - $self->conf->exists($tc.'sections_by_location', $agentnum); -} - # should be the ONLY occurrence of "Invoice" in invoice rendering code. # (except email_subject and invnum_date_pretty) sub notice_name { @@ -161,7 +155,7 @@ sub notice_name { $self->conf->config('notice_name') || 'Invoice' } -sub cust_linked { $_[0]->cust_main_custnum || $_[0]->custnum } +sub cust_linked { $_[0]->cust_main_custnum || $_[0]->custnum } sub cust_unlinked_msg { my $self = shift; "WARNING: can't find cust_main.custnum ". $self->custnum. @@ -212,7 +206,7 @@ sub insert { } -=item void +=item void [ REASON [ , REPROCESS_CDRS ] ] Voids this invoice: deletes the invoice and adds a record of the voided invoice to the FS::cust_bill_void table (and related tables starting from @@ -223,6 +217,15 @@ FS::cust_bill_pkg_void). sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + my $reprocess_cdrs = scalar(@_) ? shift : ''; + + unless (ref($reason) || !$reason) { + $reason = FS::reason->new_or_existing( + 'class' => 'I', + 'type' => 'Invoice void', + 'reason' => $reason + ); + } local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -238,7 +241,7 @@ sub void { my $cust_bill_void = new FS::cust_bill_void ( { map { $_ => $self->get($_) } $self->fields } ); - $cust_bill_void->reason($reason); + $cust_bill_void->reasonnum($reason->reasonnum) if $reason; my $error = $cust_bill_void->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; @@ -246,7 +249,7 @@ sub void { } foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { - my $error = $cust_bill_pkg->void($reason); + my $error = $cust_bill_pkg->void($reason, $reprocess_cdrs); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -269,15 +272,15 @@ sub void { # internal-only and discourage use # # =item delete -# +# # DO NOT USE THIS METHOD. Instead, apply a credit against the invoice, or use # the B method. -# +# # This is only for internal use by V, which is what you should be using. -# +# # DO NOT USE THIS METHOD. Whatever reason you think you have is almost certainly # wrong. Use B, that's what it is for. Really. This means you. -# +# # =cut sub _delete { @@ -450,9 +453,30 @@ sub previous_bill { $self->get('previous_bill'); } +=item following_bill + +Returns the customer's invoice that follows this one + +=cut + +sub following_bill { + my $self = shift; + if (!$self->get('following_bill')) { + $self->set('following_bill', qsearchs({ + table => 'cust_bill', + hashref => { + custnum => $self->custnum, + invnum => { op => '>', value => $self->invnum }, + }, + order_by => 'ORDER BY invnum ASC LIMIT 1', + })); + } + $self->get('following_bill'); +} + =item previous -Returns a list consisting of the total previous balance for this customer, +Returns a list consisting of the total previous balance for this customer, followed by the previous outstanding invoices (as FS::cust_bill objects also). =cut @@ -467,7 +491,7 @@ sub previous { qsearch( 'cust_bill', { 'custnum' => $self->custnum, #'_date' => { op=>'<', value=>$self->_date }, 'invnum' => { op=>'<', value=>$self->invnum }, - } ) + } ) ; foreach ( @cust_bill ) { $total += $_->owed; } $self->set('previous', [$total, @cust_bill]); @@ -497,7 +521,13 @@ Returns the line items (see L) for this invoice. sub cust_bill_pkg { my $self = shift; qsearch( - { 'table' => 'cust_bill_pkg', + { + 'select' => 'cust_bill_pkg.*, pkg_category.categoryname', + 'table' => 'cust_bill_pkg', + 'addl_from' => ' LEFT JOIN cust_pkg USING ( pkgnum ) '. + ' LEFT JOIN part_pkg USING ( pkgpart ) '. + ' LEFT JOIN pkg_class USING ( classnum ) '. + ' LEFT JOIN pkg_category USING ( categorynum ) ', 'hashref' => { 'invnum' => $self->invnum }, 'order_by' => 'ORDER BY billpkgnum', #important? otherwise we could use # the AUTLOADED FK search. or should @@ -608,7 +638,7 @@ sub num_cust_event { my $sql = "SELECT COUNT(*) FROM cust_event JOIN part_event USING ( eventpart ) ". " WHERE tablenum = ? AND eventtable = 'cust_bill'"; - my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql"; + my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql"; $sth->execute($self->invnum) or die $sth->errstr. " executing $sql"; $sth->fetchrow_arrayref->[0]; } @@ -628,8 +658,8 @@ Returns a list: an empty list on success or a list of errors. sub suspend { my $self = shift; - grep { $_->suspend(@_) } - grep {! $_->getfield('cancel') } + grep { $_->suspend(@_) } + grep {! $_->getfield('cancel') } $self->cust_pkg; } @@ -680,7 +710,7 @@ sub cancel { grep { $_ } map { $_->cancel(%opt) } - grep { ! $_->getfield('cancel') } + grep { ! $_->getfield('cancel') } @pkgs; } @@ -812,7 +842,7 @@ sub cust_bill_batch { =item discount_plans -Returns all discount plans (L) for this invoice, as a +Returns all discount plans (L) for this invoice, as a hash keyed by term length. =cut @@ -855,6 +885,35 @@ sub owed { $balance; } +=item owed_on_invoice + +Returns the amount to be displayed as the "Balance Due" on this +invoice. Amount returned depends on conf flags for invoicing + +See L for the true amount currently owed + +=cut + +sub owed_on_invoice { + my $self = shift; + + #return $self->owed() + # unless $self->conf->exists('previous_balance-payments_since') + + # Add charges from this invoice + my $owed = $self->charged(); + + # Add carried balances from previous invoices + # If previous items aren't to be displayed on the invoice, + # _items_previous() is aware of this and responds appropriately. + $owed += $_->{amount} for $self->_items_previous(); + + # Subtract payments and credits displayed on this invoice + $owed -= $_->{amount} for $self->_items_payments(), $self->_items_credits(); + + return $owed; +} + sub owed_pkgnum { my( $self, $pkgnum ) = @_; @@ -917,7 +976,7 @@ sub apply_payments_and_credits { $self->select_for_update; #mutex - my @payments = grep { $_->unapplied > 0 } + my @payments = grep { $_->unapplied > 0 } grep { !$_->no_auto_apply } $self->cust_main->cust_pay; my @credits = grep { $_->credited > 0 } $self->cust_main->cust_credit; @@ -946,7 +1005,7 @@ sub apply_payments_and_credits { ); my $max_credit_weight = max( map { $_->part_pkg->credit_weight || 0 } - grep { $_ } + grep { $_ } map { $_->cust_pkg } @open_lineitems ); @@ -957,7 +1016,7 @@ sub apply_payments_and_credits { } else { $app = 'credit'; } - + } elsif ( @payments ) { $app = 'pay'; } elsif ( @credits ) { @@ -1031,7 +1090,7 @@ I overrides the default email invoice From: address. I: obsolete, does nothing -I overrides "Invoice" as the name of the sent document +I overrides "Invoice" as the name of the sent document (templates from 10/2009 or newer required). I overrides the system 'lpr' option as the command to print a document @@ -1105,11 +1164,10 @@ sub queueable_email { my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) or die "invalid invoice number: " . $opt{invnum}; - if ( $opt{mode} ) { - $self->set('mode', $opt{mode}); - } + $self->set('mode', $opt{mode}) + if $opt{mode}; - my %args = map {$_ => $opt{$_}} + my %args = map {$_ => $opt{$_}} grep { $opt{$_} } qw( from notice_name no_coupon template ); @@ -1137,11 +1195,16 @@ sub email_subject { eval qq("$subject"); } +sub pdf_filename { + my $self = shift; + 'Invoice-'. $self->invnum. '.pdf'; +} + =item lpr_data HASHREF Returns the postscript or plaintext for this invoice as an arrayref. -Options must be passed as a hashref. Positional parameters are no longer +Options must be passed as a hashref. Positional parameters are no longer allowed. I