+=item request_item [ OPTIONS ]
+
+Returns a L<Business::BatchPayment::Item> object for this batch payment
+entry. This can be submitted to a processor.
+
+OPTIONS can be a list of key/values to append to the attributes. The most
+useful case of this is "process_date" to set a processing date based on the
+date the batch is being submitted.
+
+=cut
+
+sub request_item {
+ local $@;
+ my $self = shift;
+
+ eval "use Business::BatchPayment;";
+ die "couldn't load Business::BatchPayment: $@" if $@;
+
+ my $cust_main = $self->cust_main;
+ my $location = $cust_main->bill_location;
+ my $pay_batch = $self->pay_batch;
+
+ my %payment;
+ $payment{payment_type} = FS::payby->payby2bop( $pay_batch->payby );
+ if ( $payment{payment_type} eq 'CC' ) {
+ $payment{card_number} = $self->payinfo,
+ $payment{expiration} = $self->expmmyy,
+ } elsif ( $payment{payment_type} eq 'ECHECK' ) {
+ $self->payinfo =~ /(\d+)@(\d+)/; # or else what?
+ $payment{account_number} = $1;
+ $payment{routing_code} = $2;
+ $payment{account_type} = $self->paytype;
+ # XXX what if this isn't their regular payment method?
+ } else {
+ die "unsupported BatchPayment method: ".$pay_batch->payby;
+ }
+
+ my $recurring;
+ if ( $cust_main->status =~ /^active|suspended|ordered$/ ) {
+ if ( $self->payinfo_used ) {
+ $recurring = 'S'; # subsequent
+ } else {
+ $recurring = 'F'; # first use
+ }
+ } else {
+ $recurring = 'N'; # non-recurring
+ }
+
+ Business::BatchPayment->create(Item =>
+ # required
+ action => 'payment',
+ tid => $self->paybatchnum,
+ amount => $self->amount,
+
+ # customer info
+ customer_id => $self->custnum,
+ first_name => $cust_main->first,
+ last_name => $cust_main->last,
+ company => $cust_main->company,
+ address => $location->address1,
+ ( map { $_ => $location->$_ } qw(address2 city state country zip) ),
+
+ invoice_number => $self->invnum,
+ recurring_billing => $recurring,
+ %payment,
+ );
+}
+
+=item process_unbatch_and_delete
+
+L</unbatch_and_delete> run as a queued job, accepts I<$job> and I<$param>.
+
+=cut
+
+sub process_unbatch_and_delete {
+ my ($job, $param) = @_;
+ my $self = qsearchs('cust_pay_batch',{ 'paybatchnum' => scalar($param->{'paybatchnum'}) })
+ or die 'Could not find paybatchnum ' . $param->{'paybatchnum'};
+ my $error = $self->unbatch_and_delete;
+ die $error if $error;
+ return '';
+}
+
+=item unbatch_and_delete
+
+May only be called on a record with an empty status and an associated
+L<pay_batch> with a status of 'O' (not yet in transit.) Deletes all associated
+records from L<cust_bill_pay_batch> and then deletes this record.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub unbatch_and_delete {
+ my $self = shift;
+
+ return 'Cannot unbatch a cust_pay_batch with status ' . $self->status
+ if $self->status;
+
+ my $pay_batch = qsearchs('pay_batch',{ 'batchnum' => $self->batchnum })
+ or return 'Cannot find associated pay_batch record';
+
+ return 'Cannot unbatch from a pay_batch with status ' . $pay_batch->status
+ if $pay_batch->status ne 'O';
+
+ 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;
+
+ # have not generated actual payments yet, so should be safe to delete
+ foreach my $cust_bill_pay_batch (
+ qsearch('cust_bill_pay_batch',{ 'paybatchnum' => $self->paybatchnum })
+ ) {
+ my $error = $cust_bill_pay_batch->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item cust_bill
+
+Returns the invoice linked to this batched payment. Deprecated, will be
+removed.
+
+=cut
+
+sub cust_bill {
+ carp "FS::cust_pay_batch->cust_bill is deprecated";
+ my $self = shift;
+ $self->invnum ? qsearchs('cust_bill', { invnum => $self->invnum }) : '';
+}
+