+=item ticketing_queue
+
+Returns the queue name corresponding with the id from the I<ticketing_queueid>
+field, or the empty string.
+
+=cut
+
+sub ticketing_queue {
+ my $self = shift;
+ FS::TicketSystem->queue($self->ticketing_queueid);
+};
+
+=item payment_gateway [ OPTION => VALUE, ... ]
+
+Returns a payment gateway object (see L<FS::payment_gateway>) for this agent.
+
+Currently available options are I<nofatal>, I<invnum>, I<method>, and I<payinfo>.
+
+If I<nofatal> is set, and no gateway is available, then the empty string
+will be returned instead of throwing a fatal exception.
+
+If I<invnum> is set to the number of an invoice (see L<FS::cust_bill>) then
+an attempt will be made to select a gateway suited for the taxes paid on
+the invoice.
+
+The I<method> and I<payinfo> options can be used to influence the choice
+as well. Presently only 'CC' and 'ECHECK' methods are meaningful.
+
+When the I<method> is 'CC' then the card number in I<payinfo> can direct
+this routine to route to a gateway suited for that type of card.
+
+=cut
+
+sub payment_gateway {
+ my ( $self, %options ) = @_;
+
+ my $taxclass = '';
+ if ( $options{invnum} ) {
+
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $options{invnum} } );
+ die "invnum ". $options{'invnum'}. " not found" unless $cust_bill;
+
+ my @part_pkg =
+ map { $_->part_pkg }
+ grep { $_ }
+ map { $_->cust_pkg }
+ $cust_bill->cust_bill_pkg;
+
+ my @taxclasses = map $_->taxclass, @part_pkg;
+
+ $taxclass = $taxclasses[0]
+ unless grep { $taxclasses[0] ne $_ } @taxclasses; #unless there are
+ #different taxclasses
+ }
+
+ #look for an agent gateway override first
+ my $cardtype;
+ if ( $options{method} && $options{method} eq 'CC' && $options{payinfo} ) {
+ $cardtype = cardtype($options{payinfo});
+ } elsif ( $options{method} && $options{method} eq 'ECHECK' ) {
+ $cardtype = 'ACH';
+ } else {
+ $cardtype = $options{method} || '';
+ }
+
+ my $override =
+ qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => '', } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => '', } );
+
+ my $payment_gateway = new FS::payment_gateway;
+ if ( $override ) { #use a payment gateway override
+
+ $payment_gateway = $override->payment_gateway;
+
+ } else { #use the standard settings from the config
+ # the standard settings from the config could be moved to a null agent
+ # agent_payment_gateway referenced payment_gateway
+
+ my $conf = new FS::Conf;
+ unless ( $conf->exists('business-onlinepayment') ) {
+ if ( $options{'nofatal'} ) {
+ return '';
+ } else {
+ die "Real-time processing not enabled\n";
+ }
+ }
+
+ #load up config
+ my $bop_config = 'business-onlinepayment';
+ $bop_config .= '-ach'
+ if ( $options{method}
+ && $options{method} =~ /^(ECHECK|CHEK)$/
+ && $conf->exists($bop_config. '-ach')
+ );
+ my ( $processor, $login, $password, $action, @bop_options ) =
+ $conf->config($bop_config);
+ $action ||= 'normal authorization';
+ pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
+ die "No real-time processor is enabled - ".
+ "did you set the business-onlinepayment configuration value?\n"
+ unless $processor;
+
+ $payment_gateway->gateway_namespace( $conf->config('business-onlinepayment-namespace') ||
+ 'Business::OnlinePayment');
+ $payment_gateway->gateway_module($processor);
+ $payment_gateway->gateway_username($login);
+ $payment_gateway->gateway_password($password);
+ $payment_gateway->gateway_action($action);
+ $payment_gateway->set('options', [ @bop_options ]);
+
+ }
+
+ $payment_gateway;
+}
+
+=item num_prospect_cust_main
+
+Returns the number of prospects (customers with no packages ever ordered) for
+this agent.
+
+=cut
+
+sub num_prospect_cust_main {
+ shift->num_sql(FS::cust_main->prospect_sql);
+}
+
+sub num_sql {
+ my( $self, $sql ) = @_;
+ my $statement = "SELECT COUNT(*) FROM cust_main WHERE agentnum = ? AND $sql";
+ my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
+ $sth->execute($self->agentnum) or die $sth->errstr. " executing $statement";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item prospect_cust_main
+
+Returns the prospects (customers with no packages ever ordered) for this agent,
+as cust_main objects.
+
+=cut
+
+sub prospect_cust_main {
+ shift->cust_main_sql(FS::cust_main->prospect_sql);
+}
+
+sub cust_main_sql {
+ my( $self, $sql ) = @_;
+ qsearch( 'cust_main',
+ { 'agentnum' => $self->agentnum },
+ '',
+ " AND $sql"
+ );
+}
+
+=item num_active_cust_main
+
+Returns the number of active customers for this agent (customers with active
+recurring packages).
+
+=cut
+
+sub num_active_cust_main {
+ shift->num_sql(FS::cust_main->active_sql);
+}
+
+=item active_cust_main
+
+Returns the active customers for this agent, as cust_main objects.
+
+=cut
+
+sub active_cust_main {
+ shift->cust_main_sql(FS::cust_main->active_sql);
+}
+
+=item num_inactive_cust_main
+
+Returns the number of inactive customers for this agent (customers with no
+active recurring packages, but otherwise unsuspended/uncancelled).
+
+=cut
+
+sub num_inactive_cust_main {
+ shift->num_sql(FS::cust_main->inactive_sql);
+}
+
+=item inactive_cust_main
+
+Returns the inactive customers for this agent, as cust_main objects.
+
+=cut