package FS::cust_statement; use strict; use base qw( FS::cust_bill ); use FS::Record qw( dbh qsearch ); #qsearchs ); use FS::cust_main; use FS::cust_bill; use List::Util qw( sum ); =head1 NAME FS::cust_statement - Object methods for cust_statement records =head1 SYNOPSIS use FS::cust_statement; $record = new FS::cust_statement \%hash; $record = new FS::cust_statement { 'column' => 'value' }; $error = $record->insert; $error = $new_record->replace($old_record); $error = $record->delete; $error = $record->check; =head1 DESCRIPTION An FS::cust_statement object represents an informational statement which aggregates one or more invoices. FS::cust_statement inherits from FS::cust_bill. The following fields are currently supported: =over 4 =item statementnum primary key =item custnum customer =item _date date =back =head1 METHODS =over 4 =item new HASHREF Creates a new record. To add the record to the database, see L<"insert">. Note that this stores the hash reference, not a distinct copy of the hash it points to. You can ask the object for a copy with the I method. Pass "statementnum => 'ALL'" to create a temporary statement that includes all of the customer's open invoices. This statement can't be inserted and won't set the statementnum field on any invoices. Pass "invnum => number" to create a temporary statement including only the specified invoice. This is functionally the same as the invoice itself, but will be rendered using the statement template and other statement-specific options. =cut sub new { FS::Record::new(@_); } sub table { 'cust_statement'; } =item insert Adds this record to the database. If there is an error, returns the error, otherwise returns false. =cut sub insert { my $self = shift; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; local $SIG{QUIT} = 'IGNORE'; local $SIG{TERM} = 'IGNORE'; local $SIG{TSTP} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE'; my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; FS::Record::insert($self); foreach my $cust_bill ( qsearch({ 'table' => 'cust_bill', 'hashref' => { 'custnum' => $self->custnum, 'statementnum' => '', }, 'extra_sql' => 'FOR UPDATE' , }) ) { $cust_bill->statementnum( $self->statementnum ); my $error = $cust_bill->replace; if ( $error ) { $dbh->rollback if $oldAutoCommit; return "Error associating invoice: $error"; } } $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no error } =item delete Delete this record from the database. =cut sub delete { FS::Record::delete(@_); } =item replace OLD_RECORD Replaces the OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false. =cut sub replace { FS::Record::replace(@_); } sub replace_check { ''; } =item check Checks all fields to make sure this is a valid record. If there is an error, returns the error, otherwise returns false. Called by the insert and replace methods. =cut sub check { my $self = shift; my $error = $self->ut_numbern('statementnum') || $self->ut_foreign_key('custnum', 'cust_main', 'custnum' ) || $self->ut_numbern('_date') ; return $error if $error; $self->_date(time) unless $self->_date; #don't want to call cust_bill, and Record just checks virtual fields #$self->SUPER::check; ''; } =item cust_bill Returns the associated invoices (cust_bill records) for this statement. =cut sub cust_bill { my $self = shift; # we use it about a thousand times, let's cache it if ( !exists($self->{Hash}->{cust_bill}) ) { my @cust_bill; if ( $self->invnum && $self->invnum =~ /^\d+$/ ) { # one specific invoice @cust_bill = FS::cust_bill->by_key($self->invnum) or die "unknown invnum '".$self->invnum."'"; $self->set('custnum' => $cust_bill[0]->custnum); } elsif ( $self->statementnum eq 'ALL' ) { # all open invoices @cust_bill = $self->cust_main->open_cust_bill; } else { @cust_bill = qsearch('cust_bill', { statementnum => $self->statementnum } ); } $self->{Hash}->{cust_bill} = \@cust_bill; } @{ $self->{Hash}->{cust_bill} } } sub _aggregate { my( $self, $method ) = ( shift, shift ); my @agg = (); foreach my $cust_bill ( $self->cust_bill ) { push @agg, $cust_bill->$method( @_ ); } @agg; } sub _total { my( $self, $method ) = ( shift, shift ); my $total = 0; foreach my $cust_bill ( $self->cust_bill ) { $total += $cust_bill->$method( @_ ); } $total; } =item cust_bill_pkg Returns the line items (see L) for all associated invoices. =item cust_bill_pkg_pkgnum PKGNUM Returns the line items (see L) for all associated invoices and specified pkgnum. =item cust_bill_pay Returns all payment applications (see L) for all associated invoices. =item cust_credited Returns all applied credits (see L) for all associated invoices. =item cust_bill_pay_pkgnum PKGNUM Returns all payment applications (see L) for all associated invoices with matching pkgnum. =item cust_credited_pkgnum PKGNUM Returns all applied credits (see L) for all associated invoices with matching pkgnum. =cut sub cust_bill_pay { shift->_aggregate('cust_bill_pay', @_); } sub cust_credited { shift->_aggregate('cust_credited', @_); } sub cust_bill_pay_pkgnum { shift->_aggregate('cust_bill_pay_pkgnum', @_); } sub cust_credited_pkgnum { shift->_aggregate('cust_credited_pkgnum', @_); } sub cust_bill_pkg { shift->_aggregate('cust_bill_pkg', @_); } sub cust_bill_pkg_pkgnum { shift->_aggregate('cust_bill_pkg_pkgnum', @_); } =item tax Returns the total tax amount for all assoicated invoices.0 =cut =item charged Returns the total amount charged for all associated invoices. =cut =item owed Returns the total amount owed for all associated invoices. =cut sub tax { shift->_total('tax', @_); } sub charged { shift->_total('charged', @_); } sub owed { shift->_total('owed', @_); } sub enable_previous { my $self = shift; $self->conf->exists('previous_balance-show_on_statements'); } sub previous { my $self = shift; if ( $self->enable_previous ) { my @previous = grep { $_->_date < ($self->cust_bill)[0]->_date } $self->cust_main->open_cust_bill; return(sum(map {$_->owed} @previous), @previous); } else { return 0; } } =back =head1 BUGS =head1 SEE ALSO L, L, schema.html from the base documentation. =cut 1;