use File::Temp; #qw( tempfile );
use Email::Address;
use Business::CreditCard 0.28;
+use Try::Tiny;
use FS::UID qw( getotaker dbh driver_name );
use FS::Record qw( qsearchs qsearch dbdef regexp_sql );
use FS::Misc qw( generate_email send_email generate_ps do_print money_pretty card_types );
use FS::Locales;
use FS::upgrade_journal;
use FS::reason;
+use FS::DBI;
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
=item dundate
-A suggestion to events (see L<FS::part_bill_event">) to delay until this unix timestamp
+A suggestion to events (see L<FS::part_bill_event>) to delay until this unix timestamp
=item squelch_cdr
$ticket_dbh = $dbh;
} elsif ($conf->config('ticket_system') eq 'RT_External') {
my ($datasrc, $user, $pass) = $conf->config('ticket_system-rt_external_datasrc');
- $ticket_dbh = DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 });
+ $ticket_dbh = FS::DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 });
#or die "RT_External DBI->connect error: $DBI::errstr\n";
}
$ticket_dbh = $dbh;
} elsif ($conf->config('ticket_system') eq 'RT_External') {
my ($datasrc, $user, $pass) = $conf->config('ticket_system-rt_external_datasrc');
- $ticket_dbh = DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 });
+ $ticket_dbh = FS::DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 });
#or die "RT_External DBI->connect error: $DBI::errstr\n";
}
=item quiet - can be set true to supress email cancellation notices.
-=item reason - can be set to a cancellation reason (see L<FS:reason>), either a
+=item reason - can be set to a cancellation reason (see L<FS::reason>), either a
reasonnum of an existing reason, or passing a hashref will create a new reason.
The hashref should have the following keys:
typenum - Reason type (see L<FS::reason_type>)
}
dbh->commit;
- $FS::UID::AutoCommit = 1;
my @errors;
- # now cancel all services, the same way we would for individual packages.
- # if any of them fail, cancel the rest anyway.
+ # try to cancel each service, the same way we would for individual packages,
+ # but in cancel weight order.
my @cust_svc = map { $_->cust_svc } @pkgs;
my @sorted_cust_svc =
map { $_->[0] }
foreach my $cust_svc (@sorted_cust_svc) {
my $part_svc = $cust_svc->part_svc;
next if ( defined($part_svc) and $part_svc->preserve );
- my $error = $cust_svc->cancel; # immediate cancel, no date option
- push @errors, $error if $error;
+ # immediate cancel, no date option
+ # transactionize individually
+ my $error = try { $cust_svc->cancel } catch { $_ };
+ if ( $error ) {
+ dbh->rollback;
+ push @errors, $error;
+ } else {
+ dbh->commit;
+ }
}
if (@errors) {
return @errors;
}
}
my $error = $_->cancel(%lopt);
- push @errors, 'pkgnum '.$_->pkgnum.': '.$error if $error;
+ if ( $error ) {
+ dbh->rollback;
+ push @errors, 'pkgnum '.$_->pkgnum.': '.$error;
+ } else {
+ dbh->commit;
+ }
}
return @errors;
if ($options{'processing-fee'} > 0) {
my $pf_cust_pkg;
my $processing_fee_text = 'Payment Processing Fee';
+
+ unless ( $invnum ) { # probably from a payment screen
+ # do we have any open invoices? pick earliest
+ # uses the fact that cust_main->cust_bill sorts by date ascending
+ my @open = $self->open_cust_bill;
+ $invnum = $open[0]->invnum if scalar(@open);
+ }
+
+ unless ( $invnum ) { # still nothing? pick last closed invoice
+ # again uses fact that cust_main->cust_bill sorts by date ascending
+ my @closed = $self->cust_bill;
+ $invnum = $closed[$#closed]->invnum if scalar(@closed);
+ }
+
+ unless ( $invnum ) {
+ # XXX: unlikely case - pre-paying before any invoices generated
+ # what it should do is create a new invoice and pick it
+ warn '\PROCESS FEE AND NO INVOICES PICKED TO APPLY IT!';
+ return '';
+ }
+
my $pf_change_error = $self->charge({
'amount' => $options{'processing-fee'},
'pkg' => $processing_fee_text,
L<Date::Parse> for conversion functions. The empty string can be passed
to disable that time constraint completely.
-Accepts the same options as L<balance_date_sql>:
+Accepts the same options as L</balance_date_sql>:
=over 4