use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::reason_Mixin
FS::Record );
-use vars qw( $conf $unsuspendauto $me $DEBUG
+use vars qw( $conf $me $DEBUG
$otaker_upgrade_kludge $ignore_empty_reasonnum
);
use List::Util qw( min );
$FS::UID::callback{'FS::cust_credit'} = sub {
$conf = new FS::Conf;
- $unsuspendauto = $conf->exists('unsuspendauto');
};
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
- #false laziness w/ cust_pay::insert
- if ( $unsuspendauto && $old_balance && $cust_main->balance <= 0 ) {
- my @errors = $cust_main->unsuspend;
- #return
- # side-fx with nested transactions? upstack rolls back?
- warn "WARNING:Errors unsuspending customer ". $cust_main->custnum. ": ".
- join(' / ', @errors)
- if @errors;
- }
- #eslaf
+ # possibly trigger package unsuspend, doesn't abort transaction on failure
+ $self->unsuspend_balance if $old_balance;
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
);
}
-=item credit_lineitems
+=item credit_lineitems OPTIONS
+
+Creates a credit to a group of line items, with a specified amount applied
+to each. This will also calculate the tax adjustments for those amounts and
+credit the appropriate tax line items.
Example:
);
+C<billpkgnums>, C<setuprecurs>, C<amounts> are required and are parallel
+arrays. Each one indicates an amount of credit to be applied to either the
+setup or recur portion of a (non-tax) line item.
+
+C<custnum>, C<_date>, C<reasonnum>, and C<addlinfo> will be set on the
+credit before it's inserted.
+
+C<amount> is the total amount. If unspecified, the credit will be the sum
+of the per-line-item amounts and their tax adjustments.
+
=cut
#maybe i should just be an insert with extra args instead of a class method
my $error = '';
+ # first, determine the tax adjustments
+ my %tax_adjust = $class->calculate_tax_adjustment(%arg);
+ # and determine the amount automatically if it wasn't specified
+ if ( !exists( $arg{amount} ) ) {
+ $arg{amount} = sprintf('%.2f', $tax_adjust{subtotal} + $tax_adjust{taxtotal});
+ }
+
+ # create the credit
my $cust_credit = new FS::cust_credit ( {
map { $_ => $arg{$_} }
#fields('cust_credit')
- qw( custnum _date amount reasonnum addlinfo ), #pkgnum eventnum
+ qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum
} );
$error = $cust_credit->insert;
if ( $error ) {
my %cust_credit_bill_pkg = ();
my %unapplied_payments = (); #invoice numbers, and then billpaynums
- # determine the tax adjustments
- my %tax_adjust = $class->calculate_tax_adjustment(%arg);
-
foreach my $billpkgnum ( @{$arg{billpkgnums}} ) {
my $setuprecur = shift @{$arg{setuprecurs}};
my $amount = shift @{$arg{amounts}};
}
+### refund_to_unapply/unapply_refund false laziness with FS::cust_pay
+
+=item refund_to_unapply
+
+Returns L<FS::cust_credit_refund> objects that will be deleted by L</unapply_refund>
+(all currently applied refunds that aren't closed.)
+Returns empty list if credit itself is closed.
+
+=cut
+
+sub refund_to_unapply {
+ my $self = shift;
+ return () if $self->closed;
+ qsearch({
+ 'table' => 'cust_credit_refund',
+ 'hashref' => { 'crednum' => $self->crednum },
+ 'addl_from' => 'LEFT JOIN cust_refund USING (refundnum)',
+ 'extra_sql' => "AND cust_refund.closed IS NULL AND cust_refund.source_paynum IS NULL",
+ });
+}
+
+=item unapply_refund
+
+Deletes all objects returned by L</refund_to_unapply>.
+
+=cut
+
+sub unapply_refund {
+ 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;
+
+ foreach my $cust_credit_refund ($self->refund_to_unapply) {
+ my $error = $cust_credit_refund->delete;
+ if ($error) {
+ dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ dbh->commit or die dbh->errstr if $oldAutoCommit;
+ return '';
+}
+
=back
=head1 SUBROUTINES
sub process_batch_import {
my $job = shift;
+ # some false laziness with FS::cust_pay::process_batch_import
+ my $hashcb = sub {
+ my %hash = @_;
+ my $custnum = $hash{'custnum'};
+ my $agent_custid = $hash{'agent_custid'};
+ # translate agent_custid into regular custnum
+ if ($custnum && $agent_custid) {
+ die "can't specify both custnum and agent_custid\n";
+ } elsif ($agent_custid) {
+ # here is the agent virtualization
+ my $extra_sql = ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql;
+ my %search;
+ $search{'agent_custid'} = $agent_custid
+ if $agent_custid;
+ $search{'custnum'} = $custnum
+ if $custnum;
+ my $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => \%search,
+ 'extra_sql' => $extra_sql,
+ });
+ die "can't find customer with" .
+ ($custnum ? " custnum $custnum" : '') .
+ ($agent_custid ? " agent_custid $agent_custid" : '') . "\n"
+ unless $cust_main;
+ die "mismatched customer number\n"
+ if $custnum && ($custnum ne $cust_main->custnum);
+ $custnum = $cust_main->custnum;
+ }
+ $hash{'custnum'} = $custnum;
+ delete($hash{'agent_custid'});
+ return %hash;
+ };
+
my $opt = { 'table' => 'cust_credit',
'params' => [ '_date', 'credbatch' ],
'formats' => { 'simple' =>
- [ 'custnum', 'amount', 'reasonnum', 'invnum' ],
+ [ 'custnum', 'amount', 'reasonnum', 'invnum', 'agent_custid' ],
},
'default_csv' => 1,
+ 'format_hash_callbacks' => { 'simple' => $hashcb },
'postinsert_callback' => sub {
my $cust_credit = shift; #my ($cust_credit, $param ) = @_;