use FS::Record qw( qsearchs qsearch dbdef );
use FS::Misc qw( generate_email send_email generate_ps do_print );
use FS::Msgcat qw(gettext);
+use FS::payby;
use FS::cust_pkg;
use FS::cust_svc;
use FS::cust_bill;
my $dbh = dbh;
my $prepay_identifier = '';
- my( $amount, $seconds ) = ( 0, 0 );
+ my( $amount, $seconds, $upbytes, $downbytes, $totalbytes ) = (0, 0, 0, 0, 0);
my $payby = '';
if ( $self->payby eq 'PREPAY' ) {
warn " looking up prepaid card $prepay_identifier\n"
if $DEBUG > 1;
- my $error = $self->get_prepay($prepay_identifier, \$amount, \$seconds);
+ my $error = $self->get_prepay( $prepay_identifier,
+ 'amount_ref' => \$amount,
+ 'seconds_ref' => \$seconds,
+ 'upbytes_ref' => \$upbytes,
+ 'downbytes_ref' => \$downbytes,
+ 'totalbytes_ref' => \$totalbytes,
+ );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
#return "error applying prepaid card (transaction rolled back): $error";
warn " ordering packages\n"
if $DEBUG > 1;
- $error = $self->order_pkgs($cust_pkgs, \$seconds, %options);
+ $error = $self->order_pkgs( $cust_pkgs,
+ %options,
+ 'seconds_ref' => \$seconds,
+ 'upbytes_ref' => \$upbytes,
+ 'downbytes_ref' => \$downbytes,
+ 'totalbytes_ref' => \$totalbytes,
+ );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
$dbh->rollback if $oldAutoCommit;
return "No svc_acct record to apply pre-paid time";
}
+ if ( $upbytes || $downbytes || $totalbytes ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "No svc_acct record to apply pre-paid data";
+ }
if ( $amount ) {
warn " inserting initial $payby payment of $amount\n"
if $DEBUG;
my $cust_pkg = $opt->{'cust_pkg'};
- my $seconds = $opt->{'seconds'};
my $svcs = $opt->{'svcs'} || [];
my %svc_options = ();
$error = $new_cust_svc->replace($old_cust_svc);
} else {
$svc_something->pkgnum( $cust_pkg->pkgnum );
- if ( $seconds && $$seconds && $svc_something->isa('FS::svc_acct') ) {
- $svc_something->seconds( $svc_something->seconds + $$seconds );
- $$seconds = 0;
+ if ( $svc_something->isa('FS::svc_acct') ) {
+ foreach ( grep { $opt->{$_.'_ref'} && ${ $opt->{$_.'_ref'} } }
+ qw( seconds upbytes downbytes totalbytes ) ) {
+ $svc_something->$_( $svc_something->$_() + ${ $opt->{$_.'_ref'} } );
+ ${ $opt->{$_.'_ref'} } = 0;
+ }
}
$error = $svc_something->insert(%svc_options);
}
}
-=item order_pkgs HASHREF, [ SECONDSREF, [ , OPTION => VALUE ... ] ]
+#deprecated #=item order_pkgs HASHREF [ , SECONDSREF ] [ , OPTION => VALUE ... ]
+=item order_pkgs HASHREF [ , OPTION => VALUE ... ]
Like the insert method on an existing record, this method orders multiple
packages and included services atomicaly. Pass a Tie::RefHash data structure
$cust_pkg => [ $svc_acct ],
...
);
- $cust_main->order_pkgs( \%hash, \'0', 'noexport'=>1 );
+ $cust_main->order_pkgs( \%hash, 'noexport'=>1 );
Services can be new, in which case they are inserted, or existing unaudited
services, in which case they are linked to the newly-created package.
-Currently available options are: I<depend_jobnum> and I<noexport>.
+Currently available options are: I<depend_jobnum>, I<noexport>, I<seconds_ref>,
+I<upbytes_ref>, I<downbytes_ref>, and I<totalbytes_ref>.
If I<depend_jobnum> is set, all provisioning jobs will have a dependancy
on the supplied jobnum (they will not run until the specific job completes).
on the cust_main object is not recommended, as existing services will also be
reexported.)
+If I<seconds_ref>, I<upbytes_ref>, I<downbytes_ref>, or I<totalbytes_ref> is
+provided, the scalars (provided by references) will be incremented by the
+values of the prepaid card.`
+
=cut
sub order_pkgs {
my $self = shift;
my $cust_pkgs = shift;
- my $seconds = shift;
+ my $seconds_ref = ref($_[0]) ? shift : ''; #deprecated
my %options = @_;
+ $seconds_ref ||= $options{'seconds_ref'};
warn "$me order_pkgs called with options ".
join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
foreach my $cust_pkg ( keys %$cust_pkgs ) {
- my $error = $self->order_pkg( 'cust_pkg' => $cust_pkg,
- 'svcs' => $cust_pkgs->{$cust_pkg},
- 'seconds' => $seconds,
- 'depend_jobnum' => $options{'depend_jobnum'},
- );
+ my $error = $self->order_pkg(
+ 'cust_pkg' => $cust_pkg,
+ 'svcs' => $cust_pkgs->{$cust_pkg},
+ 'seconds_ref' => $seconds_ref,
+ map { $_ => $options{$_} } qw( upbytes_ref downbytes_ref totalbytes_ref
+ depend_jobnum
+ )
+ );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
FS::prepay_credit object. If there is an error, returns the error, otherwise
returns false.
-Optionally, four scalar references can be passed as well. They will have their
-values filled in with the amount, number of seconds, and number of upload and
-download bytes applied by this prepaid
-card.
+Optionally, five scalar references can be passed as well. They will have their
+values filled in with the amount, number of seconds, and number of upload,
+download, and total bytes applied by this prepaid card.
=cut
+#the ref bullshit here should be refactored like get_prepay. MyAccount.pm is
+#the only place that uses these args
sub recharge_prepay {
my( $self, $prepay_credit, $amountref, $secondsref,
$upbytesref, $downbytesref, $totalbytesref ) = @_;
my( $amount, $seconds, $upbytes, $downbytes, $totalbytes) = ( 0, 0, 0, 0, 0 );
- my $error = $self->get_prepay($prepay_credit, \$amount,
- \$seconds, \$upbytes, \$downbytes, \$totalbytes)
+ my $error = $self->get_prepay( $prepay_credit,
+ 'amount_ref' => \$amount,
+ 'seconds_ref' => \$seconds,
+ 'upbytes_ref' => \$upbytes,
+ 'downbytes_ref' => \$downbytes,
+ 'totalbytes_ref' => \$totalbytes,
+ )
|| $self->increment_seconds($seconds)
|| $self->increment_upbytes($upbytes)
|| $self->increment_downbytes($downbytes)
}
-=item get_prepay IDENTIFIER | PREPAY_CREDIT_OBJ , AMOUNTREF, SECONDSREF
+=item get_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , OPTION => VALUE ... ]
Looks up and deletes a prepaid card (see L<FS::prepay_credit>),
specified either by I<identifier> or as an FS::prepay_credit object.
-References to I<amount> and I<seconds> scalars should be passed as arguments
-and will be incremented by the values of the prepaid card.
+Available options are: I<amount_ref>, I<seconds_ref>, I<upbytes_ref>, I<downbytes_ref>, and I<totalbytes_ref>. The scalars (provided by references) will be
+incremented by the values of the prepaid card.
If the prepaid card specifies an I<agentnum> (see L<FS::agent>), it is used to
check or set this customer's I<agentnum>.
sub get_prepay {
- my( $self, $prepay_credit, $amountref, $secondsref,
- $upref, $downref, $totalref) = @_;
+ my( $self, $prepay_credit, %opt ) = @_;
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
return "removing prepay_credit (transaction rolled back): $error";
}
- $$amountref += $prepay_credit->amount;
- $$secondsref += $prepay_credit->seconds;
- $$upref += $prepay_credit->upbytes;
- $$downref += $prepay_credit->downbytes;
- $$totalref += $prepay_credit->totalbytes;
+ ${ $opt{$_.'_ref'} } += $prepay_credit->$_()
+ for grep $opt{$_.'_ref'}, qw( amount seconds upbytes downbytes totalbytes );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
scalar( grep { $self->getfield("ship_$_") ne '' } $self->addr_fields );
}
-=item all_pkgs
+=item all_pkgs [ EXTRA_QSEARCH_PARAMS_HASHREF ]
Returns all packages (see L<FS::cust_pkg>) for this customer.
sub all_pkgs {
my $self = shift;
+ my $extra_qsearch = ref($_[0]) ? shift : {};
- return $self->num_pkgs unless wantarray;
+ return $self->num_pkgs unless wantarray || keys(%$extra_qsearch);
my @cust_pkg = ();
if ( $self->{'_pkgnum'} ) {
@cust_pkg = values %{ $self->{'_pkgnum'}->cache };
} else {
- @cust_pkg = qsearch( 'cust_pkg', { 'custnum' => $self->custnum });
+ @cust_pkg = $self->_cust_pkg($extra_qsearch);
}
sort sort_packages @cust_pkg;
qsearch('cust_location', { 'custnum' => $self->custnum } );
}
-=item ncancelled_pkgs
+=item ncancelled_pkgs [ EXTRA_QSEARCH_PARAMS_HASHREF ]
Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer.
sub ncancelled_pkgs {
my $self = shift;
+ my $extra_qsearch = ref($_[0]) ? shift : {};
return $self->num_ncancelled_pkgs unless wantarray;
$self->custnum. "\n"
if $DEBUG > 1;
- @cust_pkg =
- qsearch( 'cust_pkg', {
- 'custnum' => $self->custnum,
- 'cancel' => '',
- });
- push @cust_pkg,
- qsearch( 'cust_pkg', {
- 'custnum' => $self->custnum,
- 'cancel' => 0,
- });
+ $extra_qsearch->{'extra_sql'} .= ' AND ( cancel IS NULL OR cancel = 0 ) ';
+
+ @cust_pkg = $self->_cust_pkg($extra_qsearch);
+
}
sort sort_packages @cust_pkg;
}
+sub _cust_pkg {
+ my $self = shift;
+ my $extra_qsearch = ref($_[0]) ? shift : {};
+
+ $extra_qsearch->{'select'} ||= '*';
+ $extra_qsearch->{'select'} .=
+ ',( SELECT COUNT(*) FROM cust_svc WHERE cust_pkg.pkgnum = cust_svc.pkgnum )
+ AS _num_cust_svc';
+
+ map {
+ $_->{'_num_cust_svc'} = $_->get('_num_cust_svc');
+ $_;
+ }
+ qsearch({
+ %$extra_qsearch,
+ 'table' => 'cust_pkg',
+ 'hashref' => { 'custnum' => $self->custnum },
+ });
+
+}
+
# This should be generalized to use config options to determine order.
sub sort_packages {
- if ( $a->get('cancel') and $b->get('cancel') ) {
- $a->pkgnum <=> $b->pkgnum;
- } elsif ( $a->get('cancel') or $b->get('cancel') ) {
+
+ if ( $a->get('cancel') xor $b->get('cancel') ) {
return -1 if $b->get('cancel');
return 1 if $a->get('cancel');
+ #shouldn't get here...
return 0;
} else {
- $a->pkgnum <=> $b->pkgnum;
+ my $a_num_cust_svc = $a->num_cust_svc;
+ my $b_num_cust_svc = $b->num_cust_svc;
+ return 0 if !$a_num_cust_svc && !$b_num_cust_svc;
+ return -1 if $a_num_cust_svc && !$b_num_cust_svc;
+ return 1 if !$a_num_cust_svc && $b_num_cust_svc;
+ my @a_cust_svc = $a->cust_svc;
+ my @b_cust_svc = $b->cust_svc;
+ $a_cust_svc[0]->svc_x->label cmp $b_cust_svc[0]->svc_x->label;
}
+
}
=item suspended_pkgs