package FS::cust_bill;
use strict;
-use vars qw( @ISA $DEBUG $conf $money_char );
+use vars qw( @ISA $DEBUG $me $conf $money_char );
use vars qw( $invoice_lines @buf ); #yuck
use Fcntl qw(:flock); #for spool_csv
use IPC::Run3;
use FS::cust_pay;
use FS::cust_pkg;
use FS::cust_credit_bill;
+use FS::pay_batch;
use FS::cust_pay_batch;
use FS::cust_bill_event;
use FS::part_pkg;
use FS::cust_bill_pay;
+use FS::cust_bill_pay_batch;
use FS::part_bill_event;
+use FS::payby;
@ISA = qw( FS::cust_main_Mixin FS::Record );
$DEBUG = 0;
+$me = '[FS::cust_bill]';
#ask FS::UID to run this stuff for us later
FS::UID->install_callback( sub {
qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
}
+=item open_cust_bill_pkg
+
+Returns the open line items for this invoice.
+
+Note that cust_bill_pkg with both setup and recur fees are returned as two
+separate line items, each with only one fee.
+
+=cut
+
+# modeled after cust_main::open_cust_bill
+sub open_cust_bill_pkg {
+ my $self = shift;
+
+ # grep { $_->owed > 0 } $self->cust_bill_pkg
+
+ my %other = ( 'recur' => 'setup',
+ 'setup' => 'recur', );
+ my @open = ();
+ foreach my $field ( qw( recur setup )) {
+ push @open, map { $_->set( $other{$field}, 0 ); $_; }
+ grep { $_->owed($field) > 0 }
+ $self->cust_bill_pkg;
+ }
+
+ @open;
+}
+
=item cust_bill_event
Returns the completed invoice events (see L<FS::cust_bill_event>) for this
}
-=item batch_card
+=item batch_card OPTION => VALUE...
Adds a payment for this invoice to the pending credit card batch (see
-L<FS::cust_pay_batch>).
+L<FS::cust_pay_batch>), or, if the B<realtime> option is set to a true value,
+runs the payment using a realtime gateway.
=cut
sub batch_card {
- my $self = shift;
+ my ($self, %options) = @_;
my $cust_main = $self->cust_main;
+
+ my $amount = sprintf("%.2f", $cust_main->balance - $cust_main->in_transit_payments);
+ return '' unless $amount > 0;
+
+ if ($options{'realtime'}) {
+ return $cust_main->realtime_bop ( $FS::payby::payby2bop{$cust_main->payby}, $amount,
+ %options,
+ );
+ }
+
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $pay_batch = qsearchs('pay_batch'=> '');
+ $dbh->do("LOCK TABLE pay_batch IN SHARE ROW EXCLUSIVE MODE")
+ or return "Cannot lock pay_batch: " . $dbh->errstr;
- unless ($pay_batch) {
+ my $pay_batch = qsearchs('pay_batch', {'status' => 'O'});
+
+ unless ( $pay_batch ) {
$pay_batch = new FS::pay_batch;
+ $pay_batch->setfield('status' => 'O');
my $error = $pay_batch->insert;
if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
die "error creating new batch: $error\n";
}
}
+ my $old_cust_pay_batch = qsearchs('cust_pay_batch', {
+ 'batchnum' => $pay_batch->getfield('batchnum'),
+ 'custnum' => $cust_main->getfield('custnum'),
+ } );
+
my $cust_pay_batch = new FS::cust_pay_batch ( {
'batchnum' => $pay_batch->getfield('batchnum'),
- 'invnum' => $self->getfield('invnum'),
+ 'invnum' => $self->getfield('invnum'), # is there a better value?
'custnum' => $cust_main->getfield('custnum'),
'last' => $cust_main->getfield('last'),
'first' => $cust_main->getfield('first'),
'state' => $cust_main->getfield('state'),
'zip' => $cust_main->getfield('zip'),
'country' => $cust_main->getfield('country'),
+ 'payby' => $cust_main->payby,
'payinfo' => $cust_main->payinfo,
'exp' => $cust_main->getfield('paydate'),
'payname' => $cust_main->getfield('payname'),
- 'amount' => $self->owed,
+ 'amount' => $amount, # consolidating
} );
- my $error = $cust_pay_batch->insert;
- die $error if $error;
+
+ $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum)
+ if $old_cust_pay_batch;
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ my $error;
+ if ($old_cust_pay_batch) {
+ $error = $cust_pay_batch->replace($old_cust_pay_batch)
+ } else {
+ $error = $cust_pay_batch->insert;
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
+ }
+
+ my $unapplied = $cust_main->total_credited + $cust_main->total_unapplied_payments + $cust_main->in_transit_payments;
+ foreach my $cust_bill ($cust_main->open_cust_bill) {
+ #$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ my $cust_bill_pay_batch = new FS::cust_bill_pay_batch {
+ 'invnum' => $cust_bill->invnum,
+ 'paybatchnum' => $cust_pay_batch->paybatchnum,
+ 'amount' => $cust_bill->owed,
+ '_date' => time,
+ };
+ if ($unapplied >= $cust_bill_pay_batch->amount){
+ $unapplied -= $cust_bill_pay_batch->amount;
+ next;
+ }else{
+ $cust_bill_pay_batch->amount(sprintf ( "%.2f",
+ $cust_bill_pay_batch->amount - $unapplied ));
+ $unapplied = 0;
+ }
+ $error = $cust_bill_pay_batch->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}