use Digest::MD5 qw(md5_hex);
use Date::Format;
use Business::CreditCard;
+use Time::Duration;
use FS::CGI qw(small_custview); #doh
+use FS::UI::Web;
+use FS::UI::bytecount;
use FS::Conf;
use FS::Record qw(qsearch qsearchs);
use FS::Msgcat qw(gettext);
+use FS::Misc qw(card_types);
use FS::ClientAPI_SessionCache;
use FS::svc_acct;
use FS::svc_domain;
use FS::cust_bill;
use FS::cust_main_county;
use FS::cust_pkg;
+use FS::payby;
+use HTML::Entities;
+
+#false laziness with FS::cust_main
+BEGIN {
+ eval "use Time::Local;";
+ die "Time::Local minimum version 1.05 required with Perl versions before 5.6"
+ if $] < 5.006 && !defined($Time::Local::VERSION);
+ eval "use Time::Local qw(timelocal_nocheck);";
+}
use vars qw( @cust_main_editable_fields );
@cust_main_editable_fields = qw(
county state zip country daytime night fax
ship_first ship_last ship_company ship_address1 ship_address2 ship_city
ship_state ship_zip ship_country ship_daytime ship_night ship_fax
- payby payinfo payname
+ payby payinfo payname paystart_month paystart_year payissue payip
);
use subs qw(_provision);
}
if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
- $return{payinfo} = $cust_main->payinfo_masked;
+ $return{payinfo} = $cust_main->paymask;
@return{'month', 'year'} = $cust_main->paydate_monthyear;
}
$return{'invoicing_list'} =
- join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list );
+ join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
$return{'postal_invoicing'} =
0 < ( grep { $_ eq 'POST' } $cust_main->invoicing_list );
- } else { #no customer record
+ } elsif ( $session->{'svcnum'} ) { #no customer record
my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $session->{'svcnum'} } )
or die "unknown svcnum";
$return{name} = $svc_acct->email;
+ } else {
+
+ return { 'error' => 'Expired session' }; #XXX redirect to login w/this err!
+
}
return { 'error' => '',
if ( $p->{'payby'} =~ /^(CARD|DCRD)$/ ) {
$new->paydate($p->{'year'}. '-'. $p->{'month'}. '-01');
- if ( $new->payinfo eq $cust_main->payinfo_masked ) {
+ if ( $new->payinfo eq $cust_main->paymask ) {
$new->payinfo($cust_main->payinfo);
} else {
$new->paycvv($p->{'paycvv'});
#generic
##
- my $conf = new FS::Conf;
- my %states = map { $_->state => 1 }
- qsearch('cust_main_county', {
- 'country' => $conf->config('countrydefault') || 'US'
- } );
-
use vars qw($payment_info); #cache for performance
- $payment_info ||= {
+ unless ( $payment_info ) {
- #list all counties/states/countries
- 'cust_main_county' =>
- [ map { $_->hashref } qsearch('cust_main_county', {}) ],
+ my $conf = new FS::Conf;
+ my %states = map { $_->state => 1 }
+ qsearch('cust_main_county', {
+ 'country' => $conf->config('countrydefault') || 'US'
+ } );
- #shortcut for one-country folks
- 'states' =>
- [ sort { $a cmp $b } keys %states ],
+ $payment_info = {
- 'card_types' => {
- 'VISA' => 'VISA card',
- 'MasterCard' => 'MasterCard',
- 'Discover' => 'Discover card',
- 'American Express' => 'American Express card',
- },
+ #list all counties/states/countries
+ 'cust_main_county' =>
+ [ map { $_->hashref } qsearch('cust_main_county', {}) ],
- };
+ #shortcut for one-country folks
+ 'states' =>
+ [ sort { $a cmp $b } keys %states ],
+
+ 'card_types' => card_types(),
+
+ 'paytypes' => [ @FS::cust_main::paytypes ],
+
+ 'stateid_label' => FS::Msgcat::_gettext('stateid'),
+ 'stateid_state_label' => FS::Msgcat::_gettext('stateid_state'),
+
+ 'show_ss' => $conf->exists('show_ss'),
+ 'show_stateid' => $conf->exists('show_stateid'),
+ 'show_paystate' => $conf->exists('show_bankstate'),
+ };
+
+ }
##
#customer-specific
$return{$_} = $cust_main->get($_) for qw(address1 address2 city state zip);
$return{payby} = $cust_main->payby;
+ $return{stateid_state} = $cust_main->stateid_state;
if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
- #warn $return{card_type} = cardtype($cust_main->payinfo);
+ $return{card_type} = cardtype($cust_main->payinfo);
$return{payinfo} = $cust_main->payinfo;
@return{'month', 'year'} = $cust_main->paydate_monthyear;
}
+ if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
+ my ($payinfo1, $payinfo2) = split '@', $cust_main->payinfo;
+ $return{payinfo1} = $payinfo1;
+ $return{payinfo2} = $payinfo2;
+ $return{paytype} = $cust_main->paytype;
+ $return{paystate} = $cust_main->paystate;
+
+ }
+
#doubleclick protection
my $_date = time;
$return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
or return { 'error' => gettext('illegal_text'). " paybatch: ". $p->{'paybatch'} };
my $paybatch = $1;
+ $p->{'payby'} =~ /^([A-Z]{4})$/
+ or return { 'error' => "illegal_payby " . $p->{'payby'} };
+ my $payby = $1;
+
my $payinfo;
my $paycvv = '';
- #if ( $payby eq 'CHEK' ) {
- #
- # $p->{'payinfo1'} =~ /^(\d+)$/
- # or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
- # my $payinfo1 = $1;
- # $p->{'payinfo2'} =~ /^(\d+)$/
- # or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
- # my $payinfo2 = $1;
- # $payinfo = $payinfo1. '@'. $payinfo2;
- #
- #} elsif ( $payby eq 'CARD' ) {
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+
+ $p->{'payinfo1'} =~ /^(\d+)$/
+ or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
+ my $payinfo1 = $1;
+ $p->{'payinfo2'} =~ /^(\d+)$/
+ or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
+ my $payinfo2 = $1;
+ $payinfo = $payinfo1. '@'. $payinfo2;
+
+ } elsif ( $payby eq 'CARD' || $payby eq 'DCRD' ) {
$payinfo = $p->{'payinfo'};
$payinfo =~ s/\D//g;
return { 'error' => gettext('unknown_card_type') }
if cardtype($payinfo) eq "Unknown";
- if ( defined $cust_main->dbdef_table->column('paycvv') ) {
- if ( length($p->{'paycvv'} ) ) {
- if ( cardtype($payinfo) eq 'American Express card' ) {
- $p->{'paycvv'} =~ /^(\d{4})$/
- or return { 'error' => "CVV2 (CID) for American Express cards is four digits." };
- $paycvv = $1;
- } else {
- $p->{'paycvv'} =~ /^(\d{3})$/
- or return { 'error' => "CVV2 (CVC2/CID) is three digits." };
- $paycvv = $1;
- }
+ if ( length($p->{'paycvv'}) && $p->{'paycvv'} !~ /^\s*$/ ) {
+ if ( cardtype($payinfo) eq 'American Express card' ) {
+ $p->{'paycvv'} =~ /^\s*(\d{4})\s*$/
+ or return { 'error' => "CVV2 (CID) for American Express cards is four digits." };
+ $paycvv = $1;
+ } else {
+ $p->{'paycvv'} =~ /^\s*(\d{3})\s*$/
+ or return { 'error' => "CVV2 (CVC2/CID) is three digits." };
+ $paycvv = $1;
}
}
- #} else {
- # die "unknown payby $payby";
- #}
+ } else {
+ die "unknown payby $payby";
+ }
+
+ my %payby2fields = (
+ 'CARD' => [ qw( paystart_month paystart_year payissue address1 address2 city state zip payip ) ],
+ 'CHEK' => [ qw( ss paytype paystate stateid stateid_state payip ) ],
+ );
- my $error = $cust_main->realtime_bop( 'CC', $p->{'amount'},
+ my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $p->{'amount'},
'quiet' => 1,
'payinfo' => $payinfo,
'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01',
'payname' => $payname,
'paybatch' => $paybatch,
'paycvv' => $paycvv,
- map { $_ => $p->{$_} } qw( address1 address2 city state zip )
+ map { $_ => $p->{$_} } @{ $payby2fields{$payby} }
);
return { 'error' => $error } if $error;
if ( $p->{'save'} ) {
my $new = new FS::cust_main { $cust_main->hash };
- $new->set( $_ => $p->{$_} )
- foreach qw( payname address1 address2 city state zip payinfo );
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ $new->set( $_ => $p->{$_} )
+ foreach qw( payname paystart_month paystart_year payissue payip
+ address1 address2 city state zip payinfo );
+ $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
+ } elsif ($payby eq 'CHEK' || $payby eq 'DCHK') {
+ $new->set( $_ => $p->{$_} )
+ foreach qw( payname payip paytype paystate
+ stateid stateid_state );
+ $new->set( 'payinfo' => $payinfo );
+ $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' );
+ }
$new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' );
- $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
my $error = $new->replace($cust_main);
return { 'error' => $error } if $error;
$cust_main = $new;
}
+sub process_prepay {
+
+ my $p = shift;
+
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my %return;
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my( $amount, $seconds, $upbytes, $downbytes, $totalbytes ) = ( 0, 0, 0, 0, 0 );
+ my $error = $cust_main->recharge_prepay( $p->{'prepaid_cardnum'},
+ \$amount,
+ \$seconds,
+ \$upbytes,
+ \$downbytes,
+ \$totalbytes,
+ );
+
+ return { 'error' => $error } if $error;
+
+ return { 'error' => '',
+ 'amount' => $amount,
+ 'seconds' => $seconds,
+ 'duration' => duration_exact($seconds),
+ 'upbytes' => $upbytes,
+ 'upload' => FS::UI::bytecount::bytecount_unexact($upbytes),
+ 'downbytes' => $downbytes,
+ 'download' => FS::UI::bytecount::bytecount_unexact($downbytes),
+ 'totalbytes'=> $totalbytes,
+ 'totalload' => FS::UI::bytecount::bytecount_unexact($totalbytes),
+ };
+
+}
+
sub invoice {
my $p = shift;
my $session = _cache->get($p->{'session_id'})
return { 'error' => '',
'invnum' => $invnum,
'invoice_text' => join('', $cust_bill->print_text ),
+ 'invoice_html' => $cust_bill->print_html,
};
}
+sub invoice_logo {
+ my $p = shift;
+
+ #sessioning for this? how do we get the session id to the backend invoice
+ # template so it can add it to the link, blah
+
+ my $templatename = $p->{'templatename'};
+
+ #false laziness-ish w/view/cust_bill-logo.cgi
+
+ my $conf = new FS::Conf;
+ if ( $templatename =~ /^([^\.\/]*)$/ && $conf->exists("logo_$1.png") ) {
+ $templatename = "_$1";
+ } else {
+ $templatename = '';
+ }
+
+ my $filename = "logo$templatename.png";
+
+ return { 'error' => '',
+ 'logo' => $conf->config_binary($filename),
+ 'content_type' => 'image/png', #should allow gif, jpg too
+ };
+}
+
+
sub list_invoices {
my $p = shift;
my $session = _cache->get($p->{'session_id'})
}
+sub list_svcs {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my @cust_svc = ();
+ #foreach my $cust_pkg ( $cust_main->ncancelled_pkgs ) {
+ foreach my $cust_pkg ( $p->{'ncancelled'}
+ ? $cust_main->ncancelled_pkgs
+ : $cust_main->unsuspended_pkgs ) {
+ push @cust_svc, @{[ $cust_pkg->cust_svc ]}; #@{[ ]} to force array context
+ }
+ @cust_svc = grep { $_->part_svc->svcdb eq $p->{'svcdb'} } @cust_svc
+ if $p->{'svcdb'};
+
+ #@svc_x = sort { $a->domain cmp $b->domain || $a->username cmp $b->username }
+ # @svc_x;
+
+ {
+ #no#'svcnum' => $session->{'svcnum'},
+ 'custnum' => $custnum,
+ 'svcs' => [ map {
+ my $svc_x = $_->svc_x;
+ my($label, $value) = $_->label;
+ my $part_pkg = $svc_x->cust_svc->cust_pkg->part_pkg;
+
+ { 'svcnum' => $_->svcnum,
+ 'label' => $label,
+ 'value' => $value,
+ 'username' => $svc_x->username,
+ 'email' => $svc_x->email,
+ 'seconds' => $svc_x->seconds,
+ 'upbytes' => FS::UI::bytecount::display_bytecount($svc_x->upbytes),
+ 'downbytes' => FS::UI::bytecount::display_bytecount($svc_x->downbytes),
+ 'totalbytes'=> FS::UI::bytecount::display_bytecount($svc_x->totalbytes),
+ 'recharge_amount' => $part_pkg->option('recharge_amount', 1),
+ 'recharge_seconds' => $part_pkg->option('recharge_seconds', 1),
+ 'recharge_upbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_upbytes', 1)),
+ 'recharge_downbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_downbytes', 1)),
+ 'recharge_totalbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_totalbytes', 1)),
+ # more...
+ };
+ }
+ @cust_svc
+ ],
+ };
+
+}
+
+sub list_svc_usage {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'svcnum' => $p->{'svcnum'} };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $svc_acct = qsearchs ( 'svc_acct', $search );
+ return { 'error' => 'No service selected in list_svc_usage' }
+ unless $svc_acct;
+
+ my $freq = $svc_acct->cust_svc->cust_pkg->part_pkg->freq;
+ my $start = $svc_acct->cust_svc->cust_pkg->setup;
+ #my $end = $svc_acct->cust_svc->cust_pkg->bill; # or time?
+ my $end = time;
+
+ unless($p->{beginning}){
+ $p->{beginning} = $svc_acct->cust_svc->cust_pkg->last_bill;
+ $p->{ending} = $end;
+ }
+ my @usage = ();
+
+ foreach my $part_export (
+ map { qsearch ( 'part_export', { 'exporttype' => $_ } ) }
+ qw (sqlradius sqlradius_withdomain')
+ ) {
+
+ push @usage, @ { $part_export->usage_sessions($p->{beginning},
+ $p->{ending},
+ $svc_acct)
+ };
+ }
+
+ #kinda false laziness with FS::cust_main::bill, but perhaps
+ #we should really change this bit to DateTime and DateTime::Duration
+ #
+ #change this bit to use Date::Manip? CAREFUL with timezones (see
+ # mailing list archive)
+ my ($nsec,$nmin,$nhour,$nmday,$nmon,$nyear) =
+ (localtime($p->{ending}) )[0,1,2,3,4,5];
+ my ($psec,$pmin,$phour,$pmday,$pmon,$pyear) =
+ (localtime($p->{beginning}) )[0,1,2,3,4,5];
+
+ if ( $freq =~ /^\d+$/ ) {
+ $nmon += $freq;
+ until ( $nmon < 12 ) { $nmon -= 12; $nyear++; }
+ $pmon -= $freq;
+ until ( $pmon >= 0 ) { $pmon += 12; $pyear--; }
+ } elsif ( $freq =~ /^(\d+)w$/ ) {
+ my $weeks = $1;
+ $nmday += $weeks * 7;
+ $pmday -= $weeks * 7;
+ } elsif ( $freq =~ /^(\d+)d$/ ) {
+ my $days = $1;
+ $nmday += $days;
+ $pmday -= $days;
+ } elsif ( $freq =~ /^(\d+)h$/ ) {
+ my $hours = $1;
+ $nhour += $hours;
+ $phour -= $hours;
+ } else {
+ return { 'error' => "unparsable frequency: ". $freq };
+ }
+
+ my $previous = timelocal_nocheck($psec,$pmin,$phour,$pmday,$pmon,$pyear);
+ my $next = timelocal_nocheck($nsec,$nmin,$nhour,$nmday,$nmon,$nyear);
+
+
+ {
+ 'error' => '',
+ 'svcnum' => $p->{svcnum},
+ 'beginning' => $p->{beginning},
+ 'ending' => $p->{ending},
+ 'previous' => ($previous > $start) ? $previous : $start,
+ 'next' => ($next < $end) ? $next : $end,
+ 'usage' => \@usage,
+ };
+}
+
sub order_pkg {
my $p = shift;
$svcpart ||= $cust_pkg->part_pkg->svcpart($svcdb);
my %fields = (
- 'svc_acct' => [ qw( username _password sec_phrase popnum ) ],
+ 'svc_acct' => [ qw( username domsvc _password sec_phrase popnum ) ],
'svc_domain' => [ qw( domain ) ],
'svc_external' => [ qw( id title ) ],
);
my $conf = new FS::Conf;
if ( $conf->exists('signup_server-realtime') ) {
+ my $bill_error = _do_bop_realtime( $cust_main );
+
+ if ($bill_error) {
+ $cust_pkg->cancel('quiet'=>1);
+ return $bill_error;
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ return { error => '', pkgnum => $cust_pkg->pkgnum };
+
+}
+
+sub change_pkg {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $p->{pkgnum} } )
+ or return { 'error' => "unknown package $p->{pkgnum}" };
+
+ my @newpkg;
+ my $error = FS::cust_pkg::order( $custnum,
+ [$p->{pkgpart}],
+ [$p->{pkgnum}],
+ \@newpkg,
+ );
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ my $bill_error = _do_bop_realtime( $cust_main );
+
+ if ($bill_error) {
+ $newpkg[0]->suspend;
+ return $bill_error;
+ } else {
+ $newpkg[0]->reexport;
+ }
+
+ } else {
+ $newpkg[0]->reexport;
+ }
+
+ return { error => '', pkgnum => $cust_pkg->pkgnum };
+
+}
+
+sub order_recharge {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $p->{'svcnum'} } )
+ or return { 'error' => "unknown service " . $p->{'svcnum'} };
+
+ my $svc_x = $cust_svc->svc_x;
+ my $part_pkg = $cust_svc->cust_pkg->part_pkg;
+
+ my %vhash =
+ map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_, 1) }
+ qw ( recharge_seconds recharge_upbytes recharge_downbytes
+ recharge_totalbytes );
+ my $amount = $part_pkg->option('recharge_amount', 1);
+
+ my ($l, $v, $d) = $cust_svc->label; # blah
+ my $pkg = "Recharge $v";
+
+ my $bill_error = $cust_main->charge($amount, $pkg,
+ "time: $vhash{seconds}, up: $vhash{upbytes}," .
+ "down: $vhash{downbytes}, total: $vhash{totalbytes}",
+ $part_pkg->taxclass); #meh
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') && !$bill_error ) {
+
+ $bill_error = _do_bop_realtime( $cust_main );
+
+ if ('bill_error') {
+ return $bill_error;
+ } else {
+ my $error = $svc_x->recharge (\%vhash);
+ return { 'error' => $error } if $error;
+ }
+
+ } else {
+ my $error = $bill_error;
+ $error ||= $svc_x->recharge (\%vhash);
+ return { 'error' => $error } if $error;
+ }
+
+ return { error => '', svc => $cust_svc->part_svc->svc };
+
+}
+
+sub _do_bop_realtime {
+ my ($cust_main) = @_;
+
my $old_balance = $cust_main->balance;
my $bill_error = $cust_main->bill;
- $cust_main->apply_payments;
- $cust_main->apply_credits;
- $bill_error = $cust_main->collect;
+
+ $cust_main->apply_payments_and_credits;
+ $bill_error = $cust_main->collect('realtime' => 1);
if ( $cust_main->balance > $old_balance
&& $cust_main->balance > 0
'self-service decline' );
$cust_main->apply_credits( 'order' => 'newest' );
- $cust_pkg->cancel('quiet'=>1);
return { 'error' => '_decline', 'bill_error' => $bill_error };
- } else {
- $cust_pkg->reexport;
}
- } else {
- $cust_pkg->reexport;
- }
-
- return { error => '', pkgnum => $cust_pkg->pkgnum };
-
+ '';
}
sub cancel_pkg {
}
+sub myaccount_passwd {
+ my $p = shift;
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ return { 'error' => "New passwords don't match." }
+ if $p->{'new_password'} ne $p->{'new_password2'};
+
+ return { 'error' => 'Enter new password' }
+ unless length($p->{'new_password'});
+
+ #my $search = { 'custnum' => $custnum };
+ #$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ $custnum =~ /^(\d+)$/ or die "illegal custnum";
+ my $search = " AND custnum = $1";
+ $search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
+
+ my $svc_acct = qsearchs( {
+ 'table' => 'svc_acct',
+ 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum ) '.
+ 'LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ 'LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $p->{'svcnum'}, },
+ 'extra_sql' => $search, #important
+ } )
+ or return { 'error' => "Service not found" };
+
+ $svc_acct->_password($p->{'new_password'});
+ my $error = $svc_acct->replace();
+
+ my($label, $value) = $svc_acct->cust_svc->label;
+
+ return { 'error' => $error,
+ 'label' => $label,
+ 'value' => $value,
+ };
+
+}
+
#--
sub _custoragent_session_custnum {
$context = 'customer';
$session = _cache->get($p->{'session_id'})
- or return { 'error' => "Can't resume session" }; #better error message
+ or return ( 'error' => "Can't resume session" ); #better error message
$custnum = $session->{'custnum'};
} elsif ( $p->{'agent_session_id'} ) {
'namespace' => 'FS::ClientAPI::Agent',
} );
$session = $agent_cache->get($p->{'agent_session_id'})
- or return { 'error' => "Can't resume session" }; #better error message
+ or return ( 'error' => "Can't resume session" ); #better error message
$custnum = $p->{'custnum'};
} else {
- return { 'error' => "Can't resume session" }; #better error message
+ return ( 'error' => "Can't resume session" ); #better error message
}
($context, $session, $custnum);