package FS::agent;
-use base qw( FS::m2m_Common FS::m2name_Common FS::Record );
+use base qw( FS::Commission_Mixin FS::m2m_Common FS::m2name_Common FS::Record );
use strict;
use vars qw( @ISA );
-use Business::CreditCard 0.28;
+use Business::CreditCard 0.35;
use FS::Record qw( dbh qsearch qsearchs );
use FS::cust_main;
use FS::cust_pkg;
-use FS::agent_type;
-use FS::agent_currency;
use FS::reg_code;
+use FS::agent_payment_gateway;
+use FS::payment_gateway;
use FS::TicketSystem;
use FS::Conf;
=over 4
-=item agentnum - primary key (assigned automatically for new agents)
+=item agentnum
-=item agent - Text name of this agent
+primary key (assigned automatically for new agents)
-=item typenum - Agent type (see L<FS::agent_type>)
+=item agent
-=item ticketing_queueid - Ticketing Queue
+Text name of this agent
-=item invoice_template - Invoice template name
+=item typenum
-=item agent_custnum - Optional agent customer (see L<FS::cust_main>)
+Agent type (see L<FS::agent_type>)
-=item disabled - Disabled flag, empty or 'Y'
+=item ticketing_queueid
-=item prog - Deprecated (never used)
+Ticketing Queue
-=item freq - Deprecated (never used)
+=item invoice_template
-=item username - (Deprecated) Username for the Agent interface
+Invoice template name
-=item _password - (Deprecated) Password for the Agent interface
+=item agent_custnum
+
+Optional agent customer (see L<FS::cust_main>)
+
+=item disabled
+
+Disabled flag, empty or 'Y'
+
+=item prog
+
+Deprecated (never used)
+
+=item freq
+
+Deprecated (never used)
+
+=item username
+
+(Deprecated) Username for the Agent interface
+
+=item _password
+
+(Deprecated) Password for the Agent interface
=back
|| $self->ut_textn('prog')
|| $self->ut_textn('invoice_template')
|| $self->ut_foreign_keyn('agent_custnum', 'cust_main', 'custnum' )
+ || $self->ut_numbern('ticketing_queueid')
;
return $error if $error;
Returns the FS::agent_type object (see L<FS::agent_type>) for this agent.
-=cut
-
-sub agent_type {
- my $self = shift;
- qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
-}
-
=item agent_cust_main
Returns the FS::cust_main object (see L<FS::cust_main>), if any, for this
Returns the FS::agent_currency objects (see L<FS::agent_currency>), if any, for
this agent.
-=cut
-
-sub agent_currency {
- my $self = shift;
- qsearch('agent_currency', { 'agentnum' => $self->agentnum } );
-}
-
=item agent_currency_hashref
Returns a hash references of supported additional currencies for this agent.
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>.
+Currently available options are I<nofatal>, I<method>, I<thirdparty> and I<conf>.
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> option can be used to influence the choice
+as well. Presently only CHEK/ECHECK and PAYPAL methods are meaningful.
-The I<method> and I<payinfo> options can be used to influence the choice
-as well. Presently only 'CC', 'ECHECK', and 'PAYPAL' methods are meaningful.
+If I<method> is CHEK/ECHECK and the default gateway is being returned,
+the business-onlinepayment-ach gateway will be returned if available.
-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.
+If I<thirdparty> is set and the I<method> is PAYPAL, the defined paypal
+gateway will be returned.
+
+Exisisting I<$conf> may be passed for efficiency.
=cut
+# opts invnum/payinfo for cardtype/taxclass overrides no longer supported
+# any future overrides added here need to be reconciled with the tokenization process
+
sub payment_gateway {
my ( $self, %options ) = @_;
+
+ $options{'conf'} ||= new FS::Conf;
+ my $conf = $options{'conf'};
- my $taxclass = '';
- if ( $options{invnum} ) {
-
- my $cust_bill = qsearchs('cust_bill', { 'invnum' => $options{invnum} } );
- die "invnum ". $options{'invnum'}. " not found" unless $cust_bill;
+ if ( $options{thirdparty} ) {
- 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
- }
+ # allows PayPal to coexist with credit card gateways
+ my $is_paypal = { op => '!=', value => 'PayPal' };
+ if ( uc($options{method}) eq 'PAYPAL' ) {
+ $is_paypal = 'PayPal';
+ }
- #look for an agent gateway override first
- my $cardtype = '';
- if ( $options{method} ) {
- if ( $options{method} eq 'CC' && $options{payinfo} ) {
- $cardtype = cardtype($options{payinfo});
- } elsif ( $options{method} eq 'ECHECK' ) {
- $cardtype = 'ACH';
- } elsif ( $options{method} eq 'PAYPAL' ) {
- $cardtype = 'PayPal';
+ my $gateway = qsearchs({
+ table => 'payment_gateway',
+ addl_from => ' JOIN agent_payment_gateway USING (gatewaynum) ',
+ hashref => {
+ gateway_namespace => 'Business::OnlineThirdPartyPayment',
+ gateway_module => $is_paypal,
+ disabled => '',
+ },
+ extra_sql => ' AND agentnum = '.$self->agentnum,
+ });
+
+ if ( $gateway ) {
+ return $gateway;
+ } elsif ( $options{'nofatal'} ) {
+ return '';
} else {
- $cardtype = $options{method}
+ die "no third-party gateway configured\n";
}
}
- 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;
- my $conf = new FS::Conf;
- if ( $override ) { #use a payment gateway override
-
- $payment_gateway = $override->payment_gateway;
-
- $payment_gateway->gateway_namespace('Business::OnlinePayment')
- unless $payment_gateway->gateway_namespace;
-
- } 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
-
- unless ( $conf->exists('business-onlinepayment') ) {
- if ( $options{'nofatal'} ) {
- return '';
- } else {
- die "Real-time processing not enabled\n";
- }
- }
+ my $override = qsearchs('agent_payment_gateway', { agentnum => $self->agentnum } );
- #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 = new FS::payment_gateway;
-
- $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 ]);
+ my $payment_gateway = FS::payment_gateway->by_key_or_default(
+ gatewaynum => $override ? $override->gatewaynum : '',
+ %options,
+ );
- }
+ $payment_gateway;
+}
- unless ( $payment_gateway->gateway_namespace ) {
- $payment_gateway->gateway_namespace(
- scalar($conf->config('business-onlinepayment-namespace'))
- || 'Business::OnlinePayment'
- );
- }
+=item invoice_modes
- $payment_gateway;
+Returns all L<FS::invoice_mode> objects that are valid for this agent (i.e.
+those with this agentnum or null agentnum).
+
+=cut
+
+sub invoice_modes {
+ my $self = shift;
+ qsearch( {
+ table => 'invoice_mode',
+ hashref => { agentnum => $self->agentnum },
+ extra_sql => ' OR agentnum IS NULL',
+ order_by => ' ORDER BY modename',
+ } );
}
=item num_prospect_cust_main
);
}
+=item num_ordered_cust_main
+
+Returns the number of ordered customers for this agent (customers with packages
+ordered, but not yet billed).
+
+=cut
+
+sub num_ordered_cust_main {
+ shift->num_sql(FS::cust_main->ordered_sql);
+}
+
+=item ordered_cust_main
+
+Returns the ordered customers for this agent (customers with packages ordered,
+but not yet billed), as cust_main objects.
+
+=cut
+
+sub ordered_cust_main {
+ shift->cust_main_sql(FS::cust_main->ordered_sql);
+}
+
+
=item num_active_cust_main
Returns the number of active customers for this agent (customers with active
shift->num_pkg_sql(FS::cust_pkg->cancel_sql);
}
+=item num_on_hold_cust_pkg
+
+Returns the number of inactive customer packages (one-time packages otherwise
+unsuspended/uncancelled) for this agent.
+
+=cut
+
+sub num_on_hold_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->on_hold_sql);
+}
+
+=item num_not_yet_billed_cust_pkg
+
+Returns the number of inactive customer packages (one-time packages otherwise
+unsuspended/uncancelled) for this agent.
+
+=cut
+
+sub num_not_yet_billed_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->not_yet_billed_sql);
+}
+
=item generate_reg_codes NUM PKGPART_ARRAYREF
Generates the specified number of registration codes, allowing purchase of the
$sth->fetchrow_arrayref->[0];
}
+=item num_sales
+
+Returns the number of non-disabled sales people for this agent.
+
+=cut
+
+sub num_sales {
+ my $self = shift;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM sales WHERE agentnum = ?
+ AND ( disabled = '' OR disabled IS NULL )"
+ ) or die dbh->errstr;
+ $sth->execute($self->agentnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+sub commission_where {
+ my $self = shift;
+ 'cust_credit.commission_agentnum = ' . $self->agentnum;
+}
+
+sub sales_where {
+ my $self = shift;
+ 'cust_main.agentnum = ' . $self->agentnum;
+}
=back