#use FS::UID qw(dbh);
use FS::Record qw(qsearch qsearchs dbh);
use FS::Msgcat qw(gettext);
-use FS::Misc qw(card_types);
+use FS::Misc qw(card_types money_pretty);
use FS::Misc::DateTime qw(parse_datetime);
use FS::TicketSystem;
use FS::ClientAPI_SessionCache;
use FS::msg_template;
use FS::contact;
use FS::cust_contact;
+use FS::cust_location;
+use FS::cust_payby;
-$DEBUG = 1;
+# for code organization
+use FS::ClientAPI::MyAccount::contact;
+use FS::ClientAPI::MyAccount::quotation;
+
+$DEBUG = 0;
$me = '[FS::ClientAPI::MyAccount]';
use vars qw( @cust_main_editable_fields @location_editable_fields );
),
'menu_disable' => [ $conf->config('selfservice-menu_disable',$agentnum) ],
( map { $_ => $conf->exists("selfservice-$_", $agentnum ) }
- qw( menu_skipblanks menu_skipheadings menu_nounderline no_logo )
+ qw( menu_skipblanks menu_skipheadings menu_nounderline no_logo enable_payment_without_balance )
),
( map { $_ => scalar($conf->config_binary("selfservice-$_", $agentnum)) }
qw( title_left_image title_right_image
'logo' => scalar($conf->config_binary('logo.png', $agentnum )),
( map { $_ => join("\n", $conf->config("selfservice-$_", $agentnum ) ) }
qw( head body_header body_footer company_address ) ),
+ 'money_char' => $conf->config("money_char") || '$',
'menu' => join("\n", $conf->config("ng_selfservice-menu", $agentnum ) ) ||
'main.php Home
return { error => 'Incorrect contact password.' }
unless $contact->authenticate_password($p->{'password'});
+ $session->{'contactnum'} = $contact->contactnum;
+
my @cust_contact = grep $_->selfservice_access, $contact->cust_contact;
if ( scalar(@cust_contact) == 1 ) {
$session->{'custnum'} = $cust_contact[0]->custnum;
my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
or return { error => 'Domain '. $p->{'domain'}. ' not found' };
- my $svc_acct = qsearchs( 'svc_acct', { 'username' => $p->{'username'},
- 'domsvc' => $svc_domain->svcnum, }
- );
- return { error => 'User not found.' } unless $svc_acct;
+ my @svc_acct = qsearch( 'svc_acct', { 'username' => $p->{'username'},
+ 'domsvc' => $svc_domain->svcnum, }
+ );
+
+ if ( $conf->exists('selfservice_server-login_svcpart') ) {
+ my @svcpart = $conf->config('selfservice_server-login_svcpart');
+ @svc_acct = grep { my $svcpart = $_->cust_svc->svcpart;
+ scalar( grep( $_ eq $svcpart, @svcpart ) );
+ }
+ @svc_acct;
+ }
- if($conf->exists('selfservice_server-login_svcpart')) {
- my @svcpart = $conf->config('selfservice_server-login_svcpart');
- my $svcpart = $svc_acct->cust_svc->svcpart;
- return { error => 'Invalid user.' }
- unless grep($_ eq $svcpart, @svcpart);
+ if ( $conf->exists('selfservice_server-primary_only') ) {
+ @svc_acct =
+ grep {
+ my $cust_svc = $_->cust_svc;
+ $cust_svc->cust_pkg->part_pkg->svcpart([qw( svc_acct svc_phone )])
+ == $cust_svc->svcpart
+ }
+ @svc_acct;
+ }
+
+ return { error => 'User not found.' } unless @svc_acct;
+
+ return { error => 'Multiple users.' } if scalar(@svc_acct) > 1;
+
+ my $svc_acct = $svc_acct[0];
+
+ if ( $conf->exists('selfservice_server-login_svcpart') ) {
+ my @svcpart = $conf->config('selfservice_server-login_svcpart');
+ my $svcpart = $svc_acct->cust_svc->svcpart;
+ return { error => 'Invalid user.' }
+ unless grep($_ eq $svcpart, @svcpart);
}
return { error => 'Incorrect password.' }
if ( $session->{'pkgnum'} ) {
#XXX open invoices in the pkg-balances case
} else {
+ $return{'money_char'} = $conf->config("money_char") || '$';
my @open = map {
{
- invnum => $_->invnum,
- date => time2str("%b %o, %Y", $_->_date),
- owed => $_->owed,
+ invnum => $_->invnum,
+ date => time2str("%b %o, %Y", $_->_date),
+ owed => $_->owed,
+ charged => $_->charged,
};
} $cust_main->open_cust_bill;
$return{open_invoices} = \@open;
$return{next_bill_date} ? time2str('%m/%d/%Y', $return{next_bill_date} )
: '(none)';
}
+ $return{balance_pretty} = money_pretty($return{balance});
$return{countrydefault} = scalar($conf->config('countrydefault'));
}
+ # this is here because this routine is called by both fs_ and ng_ main pages, where it appears
+ # it is not customer-specific, though it is only shown to authenticated customers
+ # it is not currently agent-specific, though at some point it might be
+ $return{'announcement'} = join(' ',$conf->config('selfservice-announcement')) || '';
+
return { 'error' => '',
'custnum' => $custnum,
%return,
}
$return{balance} = $cust_main->balance;
+ $return{balance_pretty} = money_pretty($return{balance});
$return{next_bill_date} = $cust_main->next_bill_date;
$return{next_bill_date_pretty} =
$return{next_bill_date} ? time2str('%m/%d/%Y', $return{next_bill_date} )
: '(none)';
- my @history = ();
-
my $conf = new FS::Conf;
- if ( $conf->exists('selfservice-billing_history-line_items') ) {
-
- foreach my $cust_bill ( $cust_main->cust_bill ) {
-
- push @history, {
- 'type' => 'Line item',
- 'description' => $_->desc( $cust_main->locale ).
- ( $_->sdate && $_->edate
- ? ' '. time2str('%d-%b-%Y', $_->sdate).
- ' To '. time2str('%d-%b-%Y', $_->edate)
- : ''
- ),
- 'amount' => sprintf('%.2f', $_->setup + $_->recur ),
- 'date' => $cust_bill->_date,
- 'date_pretty' => time2str('%m/%d/%Y', $cust_bill->_date ),
- }
- foreach $cust_bill->cust_bill_pkg;
-
- }
-
- } else {
+ $return{'history'} = [
+ $cust_main->payment_history(
+ 'line_items' => $conf->exists('selfservice-billing_history-line_items'),
+ 'reverse_sort' => 1,
+ )
+ ];
- push @history, {
- 'type' => 'Invoice',
- 'description' => 'Invoice #'. $_->display_invnum,
- 'amount' => sprintf('%.2f', $_->charged ),
- 'date' => $_->_date,
- 'date_pretty' => time2str('%m/%d/%Y', $_->_date ),
- }
- foreach $cust_main->cust_bill;
-
- }
-
- push @history, {
- 'type' => 'Payment',
- 'description' => 'Payment', #XXX type
- 'amount' => sprintf('%.2f', 0 - $_->paid ),
- 'date' => $_->_date,
- 'date_pretty' => time2str('%m/%d/%Y', $_->_date ),
- }
- foreach $cust_main->cust_pay;
-
- push @history, {
- 'type' => 'Credit',
- 'description' => 'Credit', #more info?
- 'amount' => sprintf('%.2f', 0 -$_->amount ),
- 'date' => $_->_date,
- 'date_pretty' => time2str('%m/%d/%Y', $_->_date ),
- }
- foreach $cust_main->cust_credit;
-
- push @history, {
- 'type' => 'Refund',
- 'description' => 'Refund', #more info? type, like payment?
- 'amount' => $_->refund,
- 'date' => $_->_date,
- 'date_pretty' => time2str('%m/%d/%Y', $_->_date ),
- }
- foreach $cust_main->cust_refund;
-
- @history = sort { $b->{'date'} <=> $a->{'date'} } @history;
-
- $return{'history'} = \@history;
+ $return{'money_char'} = $conf->config("money_char") || '$',
return \%return;
if ( $new->payinfo eq $cust_main->paymask ) {
$new->payinfo($cust_main->payinfo);
+ $new->paycvv( $p->{'paycvv'} || $cust_main->paycvv );
} else {
$new->payinfo($p->{'payinfo'});
+ return { 'error' => 'CVV2 is required' }
+ if ! $p->{'paycvv'} && $conf->exists('selfservice-onfile_require_cvv');
+ $new->paycvv( $p->{'paycvv'} )
}
$new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
- if ( $conf->exists('selfservice-onfile_require_cvv') ){
- return { 'error' => 'CVV2 is required' } unless $p->{'paycvv'};
- }
-
} elsif ( $payby =~ /^(CHEK|DCHK)$/ ) {
my $payinfo;
'require_cvv' => $conf->exists('selfservice-require_cvv'),
'onfile_require_cvv' => $conf->exists('selfservice-onfile_require_cvv'),
- 'paytypes' => [ @FS::cust_main::paytypes ],
+ 'paytypes' => [ FS::cust_payby::paytypes ],
'paybys' => [ $conf->config('signup_server-payby') ],
'cust_paybys' => \@cust_paybys,
my $payby = delete $validate->{'payby'};
- my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount,
- 'quiet' => 1,
- 'manual' => 1,
- 'selfservice' => 1,
- 'paynum_ref' => \$paynum,
- %$validate,
- );
- return { 'error' => $error } if $error;
-
- #no error, so order the fee package if applicable...
- my $conf = new FS::Conf;
- my $fee_pkgpart = $conf->config('selfservice_process-pkgpart', $cust_main->agentnum);
- my $fee_skip_first = $conf->exists('selfservice_process-skip_first');
-
- if ( $fee_pkgpart and ! $fee_skip_first || scalar($cust_main->cust_pay) ) {
-
- my $cust_pkg = new FS::cust_pkg { 'pkgpart' => $fee_pkgpart };
-
- $error = $cust_main->order_pkg( 'cust_pkg' => $cust_pkg );
- return { 'error' => "payment processed successfully, but error ordering fee: $error" }
- if $error;
-
- #and generate an invoice for it now too
- $error = $cust_main->bill( 'pkg_list' => [ $cust_pkg ] );
- return { 'error' => "payment processed and fee ordered sucessfully, but error billing fee: $error" }
- if $error;
-
- }
-
- $cust_main->apply_payments;
-
if ( $validate->{'save'} ) {
my $new = new FS::cust_main { $cust_main->hash };
if ($payby eq 'CARD' || $payby eq 'DCRD') {
stateid stateid_state );
$new->set( 'payby' => $validate->{'auto'} ? 'CHEK' : 'DCHK' );
}
- $new->set( 'payinfo' => $cust_main->card_token || $validate->{'payinfo'} );
+ $new->payinfo( $validate->{'payinfo'} ); #to properly set paymask
$new->set( 'paydate' => $validate->{'paydate'} );
my $error = $new->replace($cust_main);
if ( $error ) {
#return { 'error' => $error };
#XXX just warn verosely for now so i can figure out how these happen in
# the first place, eventually should redirect them to the "change
- #address" page but indicate the payment did process??
+ #address" page but indicate if the payment processed?
delete($validate->{'payinfo'}); #don't want to log this!
warn "WARNING: error changing customer info when processing payment (not returning to customer as a processing error): $error\n".
"NEW: ". Dumper($new)."\n".
"OLD: ". Dumper($cust_main)."\n".
"PACKET: ". Dumper($validate)."\n";
- #} else {
- #not needed...
- #$cust_main = $new;
+ } else {
+ $cust_main = $new;
}
}
+ my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount,
+ 'quiet' => 1,
+ 'manual' => 1,
+ 'selfservice' => 1,
+ 'paynum_ref' => \$paynum,
+ %$validate,
+ );
+ return { 'error' => $error } if $error;
+
+ #no error, so order the fee package if applicable...
+ my $conf = new FS::Conf;
+ my $fee_pkgpart = $conf->config('selfservice_process-pkgpart', $cust_main->agentnum);
+ my $fee_skip_first = $conf->exists('selfservice_process-skip_first');
+
+ if ( $fee_pkgpart and ! $fee_skip_first || scalar($cust_main->cust_pay) ) {
+
+ my $cust_pkg = new FS::cust_pkg { 'pkgpart' => $fee_pkgpart };
+
+ $error = $cust_main->order_pkg( 'cust_pkg' => $cust_pkg );
+ return { 'error' => "payment processed successfully, but error ordering fee: $error" }
+ if $error;
+
+ #and generate an invoice for it now too
+ $error = $cust_main->bill( 'pkg_list' => [ $cust_pkg ] );
+ return { 'error' => "payment processed and fee ordered sucessfully, but error billing fee: $error" }
+ if $error;
+
+ }
+
+ $cust_main->apply_payments;
+
my $cust_pay = '';
my $receipt_html = '';
if ($paynum) {
my @cust_bill = grep ! $_->hide, $cust_main->cust_bill;
my $balance = 0;
+ my $invoices = [
+ map {
+ #not super efficient, we also run cust_bill_pay/cust_credited inside owed
+ my @payments_and_credits = sort {$b->_date <=> $a->_date} ($_->cust_bill_pay,$_->cust_credited);
+ my $owed = $_->owed;
+ $balance += $owed;
+ +{ 'invnum' => $_->invnum,
+ '_date' => $_->_date,
+ 'date' => time2str("%b %o, %Y", $_->_date),
+ 'date_short' => time2str("%m-%d-%Y", $_->_date),
+ 'previous' => sprintf('%.2f', ($_->previous)[0]),
+ 'charged' => sprintf('%.2f', $_->charged),
+ 'owed' => sprintf('%.2f', $owed),
+ 'balance' => sprintf('%.2f', $balance),
+ 'lastpay' => @payments_and_credits
+ ? time2str("%b %o, %Y", $payments_and_credits[0]->_date)
+ : '',
+ }
+ } @cust_bill
+ ];
return { 'error' => '',
'balance' => $cust_main->balance,
- 'invoices' => [
- map {
- my $owed = $_->owed;
- $balance += $owed;
- +{ 'invnum' => $_->invnum,
- '_date' => $_->_date,
- 'date' => time2str("%b %o, %Y", $_->_date),
- 'date_short' => time2str("%m-%d-%Y", $_->_date),
- 'previous' => sprintf('%.2f', ($_->previous)[0]),
- 'charged' => sprintf('%.2f', $_->charged),
- 'owed' => sprintf('%.2f', $owed),
- 'balance' => sprintf('%.2f', $balance),
- }
- }
- @cust_bill
- ],
+ 'money_char' => $conf->config("money_char") || '$',
+ 'invoices' => $invoices,
'legacy_invoices' => [
map {
+{ 'legacyinvnum' => $_->legacyinvnum,
}
# no usage to hide here
- } elsif ( $svcdb eq 'svc_phone' ) {
+ } elsif ( $svcdb eq 'svc_phone' or $svcdb eq 'svc_pbx' ) {
if (!$hide_usage) {
# could potentially show lots of things...
$hash{'outbound'} = 1;
$hash{'inbound'} = 0;
- if ( $part_pkg->plan eq 'voip_inbound' ) {
- $hash{'outbound'} = 0;
- $hash{'inbound'} = 1;
- } elsif ( $part_pkg->option('selfservice_inbound_format')
- or $conf->config('selfservice-default_inbound_cdr_format')
- ) {
- $hash{'inbound'} = 1;
+ if ( $svcdb eq 'svc_phone' ) {
+ if ( $part_pkg->plan eq 'voip_inbound' ) {
+ $hash{'outbound'} = 0;
+ $hash{'inbound'} = 1;
+ } elsif ( $part_pkg->option('selfservice_inbound_format')
+ or $conf->config('selfservice-default_inbound_cdr_format')
+ ) {
+ $hash{'inbound'} = 1;
+ }
}
foreach (qw(inbound outbound)) {
# hmm...we can't filter by status here, because there might
# XXX CDR type support...
# XXX any way to do a paged search on this?
# we have to return the results all at once...
- my($svc_phone, $begin, $end, %opt) = @_;
+ my($svc_x, $begin, $end, %opt) = @_;
map [ $_->downstream_csv(%opt, 'keeparray' => 1) ],
- $svc_phone->get_cdrs(
- 'begin'=>$begin,
- 'end'=>$end,
+ $svc_x->get_cdrs(
+ 'begin' => $begin,
+ 'end' => $end,
'disable_charged_party' => 1,
%opt
);
sub list_cdr_usage {
my $p = shift;
- _usage_details( \&_list_cdr_usage, $p,
- 'svcdb' => 'svc_phone',
- );
+ _usage_details( \&_list_cdr_usage, $p );
}
sub _usage_details {
my $search = { 'svcnum' => $p->{'svcnum'} };
$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
- my $svcdb = $opt{'svcdb'} || 'svc_acct';
-
- my $svc_x = qsearchs( $svcdb, $search );
+ my $cust_svc = qsearchs( 'cust_svc', $search );
return { 'error' => 'No service selected in list_svc_usage' }
- unless $svc_x;
+ unless $cust_svc;
- my $cust_pkg = $svc_x->cust_svc->cust_pkg;
+ my $svc_x = $cust_svc->svc_x;
+ my $svcdb = $svc_x->table;
+ my $cust_pkg = $cust_svc->cust_pkg;
my $freq = $cust_pkg->part_pkg->freq;
my %callback_opt;
my $header = [];
- if ( $svcdb eq 'svc_phone' ) {
+ if ( $svcdb eq 'svc_phone' or $svcdb eq 'svc_pbx' ) {
my $format = '';
if ( $p->{inbound} ) {
$format = $cust_pkg->part_pkg->option('selfservice_inbound_format')
or return { 'error' => "unknown custnum $custnum" };
my $status = $cust_main->status;
+
+ my %order_pkg_options = ();
+ if ( $p->{locationnum} > 0 ) {
+ $order_pkg_options{locationnum} = delete($p->{locationnum});
+ } elsif ( $p->{address1} ) {
+ $order_pkg_options{'cust_location'} = new FS::cust_location {
+ map { $_ => $p->{$_} }
+ qw( address1 address2 city county state zip country )
+ };
+ }
+
#false laziness w/ClientAPI/Signup.pm
my $cust_pkg = new FS::cust_pkg ( {
- 'custnum' => $custnum,
- 'pkgpart' => $p->{'pkgpart'},
+ 'custnum' => $custnum,
+ 'pkgpart' => $p->{'pkgpart'},
+ 'quantity' => $p->{'quantity'} || 1,
} );
my $error = $cust_pkg->check;
return { 'error' => $error } if $error;
}
- use Tie::RefHash;
- tie my %hash, 'Tie::RefHash';
- %hash = ( $cust_pkg => \@svc );
- #msgcat
- $error = $cust_main->order_pkgs( \%hash, 'noexport' => 1 );
+ $error = $cust_main->order_pkg(
+ 'cust_pkg' => $cust_pkg,
+ 'svcs' => \@svc,
+ 'noexport' => 1,
+ %order_pkg_options,
+ );
return { 'error' => $error } if $error;
my $conf = new FS::Conf;
return { error=>"Can't change a suspended package", pkgnum=>$cust_pkg->pkgnum}
if $cust_pkg->status eq 'suspended';
- my @newpkg;
- my $error = FS::cust_pkg::order( $custnum,
- [$p->{pkgpart}],
- [$p->{pkgnum}],
- \@newpkg,
- );
+ my $err_or_cust_pkg = $cust_pkg->change( 'pkgpart' => $p->{'pkgpart'},
+ 'quantity' => $p->{'quantity'} || 1,
+ );
+
+ return { error=>$err_or_cust_pkg, pkgnum=>$cust_pkg->pkgnum }
+ unless ref($err_or_cust_pkg);
if ( $conf->exists('signup_server-realtime') ) {
- my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_credit'=>1 );
+ my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_invoice_void'=>1 );
if ($bill_error) {
- $newpkg[0]->suspend;
+ $err_or_cust_pkg->suspend;
return $bill_error;
} else {
- $newpkg[0]->reexport;
+ $err_or_cust_pkg->reexport;
}
} else {
- $newpkg[0]->reexport;
+ $err_or_cust_pkg->reexport;
}
return { error => '', pkgnum => $cust_pkg->pkgnum };
my $old_balance = $cust_main->balance;
- my $bill_error = $cust_main->bill
- || $cust_main->apply_payments_and_credits;
+ my @cust_bill;
+ my $bill_error = $cust_main->bill(
+ 'return_bill' => \@cust_bill,
+ );
+
+ $bill_error ||= $cust_main->apply_payments_and_credits;
$bill_error ||= $cust_main->realtime_collect('selfservice' => 1)
if $cust_main->payby =~ /^(CARD|CHEK)$/;
)
)
{
- unless ( $opt{'no_credit'} ) {
- #this makes sense. credit is "un-doing" the invoice
- my $conf = new FS::Conf;
- $cust_main->credit( sprintf("%.2f", $cust_main->balance-$old_balance ),
- 'self-service decline',
- reason_type=>$conf->config('signup_credit_type'),
- );
- $cust_main->apply_credits( 'order' => 'newest' );
+ unless ( $opt{'no_invoice_void'} ) {
+
+ #this used to apply a credit, but now we can void invoices...
+ foreach my $cust_bill (@cust_bill) {
+ my $voiderror = $cust_bill->void('automatic payment failed');
+ warn "Error voiding cust bill after decline: $voiderror" if $voiderror;
+ }
+
}
return { 'error' => '_decline', 'bill_error' => $bill_error };
}
sub provision_phone {
- my $p = shift;
- my @bulkdid;
- @bulkdid = @{$p->{'bulkdid'}} if $p->{'bulkdid'};
+ my $p = shift;
+ my @bulkdid;
+ @bulkdid = @{$p->{'bulkdid'}} if $p->{'bulkdid'};
- if($p->{'svcnum'} && $p->{'svcnum'} =~ /^\d+$/){
+ #editing an existing phone number
+ if ( $p->{'svcnum'} && $p->{'svcnum'} =~ /^\d+$/ ) {
my($context, $session, $custnum) = _custoragent_session_custnum($p);
return { 'error' => $session } if $context eq 'error';
return { 'error' => $svc_phone->replace };
}
-# single DID LNP
- unless($p->{'lnp'}) {
+ # single DID LNP
+ unless ( $p->{'lnp'} ) {
$p->{'lnp_desired_due_date'} = parse_datetime($p->{'lnp_desired_due_date'});
$p->{'lnp_status'} = "portingin";
return _provision( 'FS::svc_phone',
$p,
@_
);
- }
+ }
-# single DID order
- unless (scalar(@bulkdid)) {
+ # single DID order (the usual case)
+ unless (scalar(@bulkdid)) {
return _provision( 'FS::svc_phone',
[qw(phonenum countrycode)],
[qw(phonenum countrycode)],
$p,
@_
);
- }
+ }
-# bulk DID order case
+ # bulk DID order case
my $error;
foreach my $did ( @bulkdid ) {
$did =~ s/[^0-9]//g;
{ 'bulkdid' => [ @bulkdid ], 'svc' => $error->{'svc'} }
}
+sub provision_pbx {
+ my $p = shift;
+ warn "provision_pbx called\n"
+ if $DEBUG;
+
+ warn "provision_pbx calling _provision\n"
+ if $DEBUG;
+ _provision( 'FS::svc_pbx',
+ [qw(id title max_extensions max_simultaneous ip_addr)],
+ [qw(id title max_extensions max_simultaneous ip_addr)],
+ $p,
+ @_
+ );
+}
+
sub provision_acct {
my $p = shift;
warn "provision_acct called\n"
);
}
+sub provision_forward {
+ my $p = shift;
+ _provision( 'FS::svc_forward',
+ ['srcsvc','src','dstsvc','dst'],
+ [],
+ $p,
+ );
+}
+
sub _provision {
my( $class, $fields, $return_fields, $p ) = splice(@_, 0, 4);
warn "_provision called for $class\n"
my $part_svc = qsearchs('part_svc', { 'svcpart' => $p->{'svcpart'} } )
or return { 'error' => "unknown svcpart $p->{'svcpart'}" };
+ return { error=> 'svcpart '. $p->{'svcpart'}. " is not a $class definition" }
+ if $class ne 'FS::'. $part_svc->svcdb;
+
warn "creating $class record\n"
if $DEBUG;
my $svc_x = $class->new( {
'svcpart' => $p->{'svcpart'},
map { $_ => $p->{$_} } @$fields
} );
+
+ my %insert_args = ();
+ #i shouldn't be a special case here (pass an option or something)
+ if ( $class eq 'FS::svc_phone'
+ && grep length($p->{$_}), @location_editable_fields
+ )
+ {
+ $insert_args{'cust_location'} = new FS::cust_location {
+ map { $_ => $p->{$_} } @location_editable_fields
+ };
+ }
+
warn "inserting $class record\n"
if $DEBUG;
- my $error = $svc_x->insert;
+ my $error = $svc_x->insert(%insert_args);
unless ( $error ) {
warn "finding inserted record for svcnum ". $svc_x->svcnum. "\n"
}
}
+ if ($ret->{'svcdb'} eq 'svc_forward') {
+ $ret->{'forward_emails'} = {$cust_pkg->forward_emails()};
+ }
+
$ret;
}
)
&& ! $svc_acct->check_password($p->{'old_password'});
+ # should move password length checks into is_password_allowed
$error = 'Password too short.'
if length($p->{'new_password'}) < ($conf->config('passwordmin') || 6);
$error = 'Password too long.'
if length($p->{'new_password'}) > ($conf->config('passwordmax') || 8);
- $svc_acct->set_password($p->{'new_password'});
- $error ||= $svc_acct->replace();
+ $error ||= $svc_acct->is_password_allowed($p->{'new_password'})
+ || $svc_acct->set_password($p->{'new_password'})
+ || $svc_acct->replace();
#regular pw change in self-service should change contact pw too, otherwise its
#way too confusing. hell its confusing they're separate at all, but alas.
}
-# sub contact_passwd {
-# my $p = shift;
-# my($context, $session, $custnum) = _custoragent_session_custnum($p);
-# return { 'error' => $session } if $context eq 'error';
-#
-# return { 'error' => 'Not logged in as a contact.' }
-# unless $session->{'contactnum'};
-#
-# 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 selfservice_access IS NOT NULL ".
-# " AND selfservice_access = 'Y' ".
-# " AND ( disabled IS NULL OR disabled = '' )".
-# " AND custnum IS NOT NULL AND custnum = $1";
-# $search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
-#
-# my $contact = qsearchs( {
-# 'table' => 'contact',
-# 'addl_from' => 'LEFT JOIN cust_main USING ( custnum ) ',
-# 'hashref' => { 'contactnum' => $session->{'contactnum'}, },
-# 'extra_sql' => $search, #important
-# } )
-# or return { 'error' => "Email not found" }; #? how did we get logged in?
-# # deleted since then?
-#
-# my $error = '';
-#
-# # use these svc_acct length restrictions??
-# my $conf = new FS::Conf;
-# $error = 'Password too short.'
-# if length($p->{'new_password'}) < ($conf->config('passwordmin') || 6);
-# $error = 'Password too long.'
-# if length($p->{'new_password'}) > ($conf->config('passwordmax') || 8);
-#
-# $error ||= $contact->change_password($p->{'new_password'});
-#
-# return { 'error' => $error, };
-#
-# }
-
sub reset_passwd {
my $p = shift;
my($username, $domain) = split('@', $p->{'email'});
my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } );
if ( $svc_domain ) {
- $svc_acct = qsearchs('svc_acct', { 'username' => $p->{'username'},
+ $svc_acct = qsearchs('svc_acct', { 'username' => $username,
'domsvc' => $svc_domain->svcnum }
);
if ( $svc_acct ) {
my $reset_session = {
'svcnum' => $svc_acct->svcnum,
- 'agentnum' =>
+ 'agentnum' => $svc_acct->cust_main->agentnum,
};
my $timeout = '1 hour'; #?
if ( $svc_acct ) {
- $svc_acct->set_password($p->{'new_password'});
- my $error = $svc_acct->replace();
+ my $error ||= $svc_acct->is_password_allowed($p->{'new_password'})
+ || $svc_acct->set_password($p->{'new_password'})
+ || $svc_acct->replace();
return { %$info, 'error' => $error } if $error;