my $payinfo2 = $1;
$payinfo = $payinfo1. '@'. $payinfo2;
- $payinfo = $cust_main->payinfo
- if $cust_main->paymask eq $payinfo;
-
my $achonfile = 0;
if ( $cust_main->paymask eq $payinfo ) {
$payinfo = $cust_main->payinfo;
};
}
-sub list_payby {
- my $p = shift;
-
- my($context, $session, $custnum) = _custoragent_session_custnum($p);
- return { 'error' => $session } if $context eq 'error';
-
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
- or return { 'error' => "unknown custnum $custnum" };
-
- return {
- 'payby' => [ map {
- my $cust_payby = $_;
- +{
- map { $_ => $cust_payby->$_ }
- qw( custpaybynum weight payby paymask paydate
- payname paystate paytype
- )
- };
- }
- $cust_main->cust_payby
- ],
- };
-}
-
-sub insert_payby {
- my $p = shift;
-
- my($context, $session, $custnum) = _custoragent_session_custnum($p);
- return { 'error' => $session } if $context eq 'error';
-
- #XXX payinfo1 + payinfo2 for CHEK?
- #or take the opportunity to use separate, more well- named fields?
- # my $payinfo;
- # $p->{'payinfo1'} =~ /^([\dx]+)$/
- # or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
- # my $payinfo1 = $1;
- # $p->{'payinfo2'} =~ /^([\dx\.]+)$/ # . turned on by echeck-country CA ?
- # or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
- # my $payinfo2 = $1;
- # $payinfo = $payinfo1. '@'. $payinfo2;
-
- my $cust_payby = new FS::cust_payby {
- 'custnum' => $custnum,
- map { $_ => $p->{$_} } qw( weight payby payinfo paycvv paydate payname
- paystate paytype payip
- ),
- };
-
- my $error = $cust_payby->insert;
- if ( $error ) {
- return { 'error' => $error };
- } else {
- return { 'custpaybynum' => $cust_payby->custpaybynum };
- }
-
-}
-
-sub update_payby {
- my $p = shift;
-
- my($context, $session, $custnum) = _custoragent_session_custnum($p);
- return { 'error' => $session } if $context eq 'error';
-
- my $cust_payby = qsearchs('cust_payby', {
- 'custnum' => $custnum,
- 'custpaybynum' => $p->{'custpaybynum'},
- })
- or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
-
- foreach my $field (
- qw( weight payby payinfo paycvv paydate payname paystate paytype payip )
- ) {
- next unless exists($p->{$field});
- $cust_payby->set($field,$p->{$field});
- }
-
- my $error = $cust_payby->replace;
- if ( $error ) {
- return { 'error' => $error };
- } else {
- return { 'custpaybynum' => $cust_payby->custpaybynum };
- }
-
-}
-
-sub verify_payby {
- my $p = shift;
-
- my($context, $session, $custnum) = _custoragent_session_custnum($p);
- return { 'error' => $session } if $context eq 'error';
-
- my $cust_payby = qsearchs('cust_payby', {
- 'custnum' => $custnum,
- 'custpaybynum' => $p->{'custpaybynum'},
- })
- or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
-
- return { 'error' => $cust_payby->verify };
-
-}
-
-sub delete_payby {
- my $p = shift;
-
- my($context, $session, $custnum) = _custoragent_session_custnum($p);
- return { 'error' => $session } if $context eq 'error';
-
- my $cust_payby = qsearchs('cust_payby', {
- 'custnum' => $custnum,
- 'custpaybynum' => $p->{'custpaybynum'},
- })
- or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
-
- my $conf = new FS::Conf;
- if (($cust_payby->payby eq "DCHK" || $cust_payby->payby eq "CHEK") && $conf->exists('selfservice-ACH_info_readonly')) {
- return { 'error' => "Sorry you do not have permission to delete bank information." };
- }
- else {
- return { 'error' => $cust_payby->delete };
- }
-}
-
sub cancel {
my $p = shift;
my $session = _cache->get($p->{'session_id'})
$self->ip_addr('');
}
+ # Will strip extraneous leading zeros from ip adddresses
+ # e.g. 10.0.022.220 corrected to 10.0.22.220
+ $self->ut_ip46n('ip_addr');
+
if ( $self->ip_addr
and !$self->router
and $self->conf->exists('auto_router') ) {
#regular FS::TABLE methods
#on it.
+C<$FS::Record::qsearch_qualify_columns> package global is disabled by default.
+When enabled, the WHERE clause generated from the 'hashref' parameter has
+the table name prepended to each column name. WHERE column = 'value' becomes
+WHERE table.coumn = 'value'
+
=cut
my %TYPE = (); #for debugging
$self->getfield($field) =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
or return "Illegal (IP address) $field: ". $self->getfield($field);
for ( $1, $2, $3, $4 ) { return "Illegal (IP address) $field" if $_ > 255; }
- $self->setfield($field, "$1.$2.$3.$4");
+ $self->setfield( $field, $self->_ut_ip_strip_leading_zeros( "$1.$2.$3.$4" ));
'';
}
sub ut_ip46 {
my( $self, $field ) = @_;
- my $ip = NetAddr::IP->new($self->getfield($field))
- or return "Illegal (IP address) $field: ".$self->getfield($field);
+ my $ip = NetAddr::IP->new(
+ $self->_ut_ip_strip_leading_zeros( $self->getfield($field) )
+ ) or return "Illegal (IP address) $field: ".$self->getfield($field);
$self->setfield($field, lc($ip->addr));
return '';
}
$self->ut_ip46($field);
}
+sub _ut_ip_strip_leading_zeros {
+ # strip user-entered leading 0's from IP addresses
+ # so parsers like NetAddr::IP don't mangle the address
+ # e.g. NetAddr::IP converts 10.0.022.220 into 10.0.18.220
+
+ my ( $self, $ip ) = @_;
+
+ return join '.', map int, split /\./, $ip
+ if $ip
+ && $ip =~ /\./
+ && $ip =~ /[\.^]0/;
+ $ip;
+}
+
=item ut_coord COLUMN [ LOWER [ UPPER ] ]
Check/untaint coordinates.
'min_level', 'int', 'NULL', '', '', '',
'msgnum', 'int', '', '', '', '',
'to_addr', 'varchar', 'NULL', 255, '', '',
+ 'context_height', 'int', 'NULL', '', '', '',
],
'primary_key' => 'logemailnum',
'unique' => [],
if ( @tagnums ) {
if ( $params->{'all_tags'} ) {
+ my $exists = $params->{'all_tags'} eq 'all' ? 'exists' : 'not exists';
foreach ( @tagnums ) {
- push @where, 'exists(select 1 from cust_tag where '.
+ push @where, $exists.'(select 1 from cust_tag where '.
'cust_tag.custnum = cust_main.custnum and tagnum = '.
$_ . ')';
}
use base qw( FS::Record );
use FS::Record qw( qsearch qsearchs dbdef );
use FS::UID qw( dbh driver_name );
+use FS::Log;
use FS::log_context;
use FS::log_email;
use FS::upgrade_journal;
sub insert {
# not using process_o2m for this, because we don't have a web interface
my $self = shift;
+
my $error = $self->SUPER::insert;
return $error if $error;
- my $contexts = {}; #for quick checks when sending emails
- foreach ( @_ ) {
+
+ my $contexts = {};
+ my $context_height = @_;
+ foreach ( @_ ) { # ordered from least to most specific
my $context = FS::log_context->new({
'lognum' => $self->lognum,
'context' => $_
});
$error = $context->insert;
return $error if $error;
- $contexts->{$_} = 1;
+ $contexts->{$_} = $context_height--;
}
+
foreach my $log_email (
qsearch('log_email',
{
}
)
) {
- # shouldn't be a lot of these, so not packing this into the qsearch
+ # shouldn't be a lot of log_email records, so not packing these checks into the qsearch
next if $log_email->context && !$contexts->{$log_email->context};
+ next if $log_email->context_height && ($contexts->{$log_email->context} > $log_email->context_height);
my $msg_template = qsearchs('msg_template',{ 'msgnum' => $log_email->msgnum });
unless ($msg_template) {
warn "Could not send email when logging, could not load message template for logemailnum " . $log_email->logemailnum;
next;
}
my $emailerror = $msg_template->send(
- 'msgtype' => 'admin',
- 'to' => $log_email->to_addr,
+ 'msgtype' => 'admin',
+ 'to' => $log_email->to_addr,
'substitutions' => {
- 'loglevel' => $FS::Log::LEVELS{$self->level}, # which has hopefully been loaded...
- 'logcontext' => $log_email->context, # use the one that triggered the email
+ 'loglevel' => $FS::Log::LEVELS{$self->level} || 'unknown',
+ 'logcontext' => join(', ', keys( %$contexts )) || 'unknown',
'logmessage' => $self->message,
},
);
=item to_addr - who the email will be sent to (in addition to any bcc on the template)
+=item context_height - number of context stack levels to match against
+(0 or null matches against full stack, 1 only matches lowest level context, 2 matches lowest two levels, etc.)
+
=back
=head1 METHODS
|| $self->ut_number('min_level')
|| $self->ut_foreign_key('msgnum', 'msg_template', 'msgnum')
|| $self->ut_textn('to_addr')
+ || $self->ut_numbern('context_height')
;
return $error if $error;
my $reasonnum = $self->option('reasonnum');
- my $desc = 'from invoice #'. $cust_bill->display_invnum .
+ my $desc = ' for customer #'.$cust_main->display_custnum.': '.$cust_main->name.' from invoice #'. $cust_bill->display_invnum .
' ('. time2str($date_format, $cust_bill->_date) . ')';
# could also show custnum and pkgnums here?
my $error = $sales_cust_main->credit(
<% include( 'elements/browse.html',
'title' => 'Discounts',
'name' => 'discounts',
- 'menubar' => [ 'Add a new discount' =>
- $p.'edit/discount.html',
- ],
- 'query' => { 'table' => 'discount', },
+ 'menubar' => \@menubar,
+ 'query' => \%query,
+ 'order_by_sql' => { description => 'discountnum' },
'count_query' => 'SELECT COUNT(*) FROM discount',
'disableable' => 1,
'disabled_statuspos' => 1,
'classname',
'description',
],
- 'links' => [ $link,
- $link,
- ],
+ 'links' => \@links
)
%>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-my $link = [ "${p}edit/discount.html?", 'discountnum' ];
+my @links = (
+ [ "${p}edit/discount.html?", 'discountnum' ],
+ [ "${p}edit/discount_class.html?", 'classnum' ],
+);
+
+# Fixes disableable, because discount and discount_class tables
+# both contain a 'disabled' column
+local $FS::Record::qsearch_qualify_columns = 1;
+
+my %query = (
+ select => 'discount.*, discount_class.*',
+ table => 'discount',
+ addl_from => 'LEFT JOIN discount_class USING(classnum)',
+);
+
+my @menubar = (
+ 'Add a new discount' => $p.'edit/discount.html',
+ 'Discount classes' => $p.'browse/discount_class.html',
+);
</%init>
. $add_condition_link
. ' | '
. $system_log_link
- . '</P>'
- . '<SCRIPT>'
- . $areyousure
- . '</SCRIPT>',
+ . '</P>',
'query' => $query,
'count_query' => $count_query,
'header' => [ '#',
) %>
+<script>
+ function areyousure_delete_log_email(logemailnum) {
+ if ( confirm( 'Delete log email condition #' + logemailnum )) {
+ <%
+ include(
+ '/elements/popup_link_onclick.html' => {
+ js_action => qq( '${fsurl}/misc/delete-log_email.html?logemailnum=' + logemailnum ),
+ actionlabel => 'Delete log email condition',
+ nofalse => 1,
+ }
+ )
+ %>
+ return;
+ }
+ }
+</script>
+
<%init>
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
-my $add_condition_link = include('/elements/popup_link.html',
- 'action' => $p.'edit/log_email.html?popup=1',
- 'label' => 'Add log email condition',
- 'actionlabel' => 'Add log email condition',
-);
+my $add_condition_link = qq( <a href="${fsurl}edit/log_email.html">Add log email condition</a> );
my $system_log_link = qq(<A HREF="${p}search/log.html">System Log</A>);
my $count_query = "SELECT COUNT(*) FROM log_email";
my $actions = sub {
- my $log_email = shift;
- my $logemailnum = $log_email->logemailnum;
+ my $logemailnum = shift->logemailnum;
qq!<A HREF="javascript:areyousure_delete_log_email($logemailnum)">(delete)</A>!;
};
-my $areyousure_onclick = include('/elements/popup_link_onclick.html',
- 'js_action' => q(') . $p . q(misc/delete-log_email.html?logemailnum=' + logemailnum),
- 'actionlabel' => 'Delete log email condition',
-);
-
-my $areyousure = <<EOF;
-function areyousure_delete_log_email(logemailnum) {
- if (confirm('Are you sure you want to delete log email condition #'+logemailnum+'?')) {
-${areyousure_onclick}
- }
-}
-EOF
-
my $editlink = [ $p.'edit/log_email.html?logemailnum=', 'logemailnum' ];
</%init>
'labels' => { '' => '(all)', map { $_ => $_ } @contexts },
'curr_value' => scalar($cgi->param('context')),
},
+ { 'field' => 'context_height',
+ 'type' => 'checkbox',
+ 'postfix' => 'Only match most specific context',
+ 'value' => 1,
+ 'curr_value' => scalar($cgi->param('context_height')),
+ },
{ 'field' => 'min_level',
'type' => 'select',
'options' => [ &FS::Log::levelnums ],
],
'labels' => {
'context' => 'Context',
+ 'context_height' => '',
'min_level' => 'Min. Level',
'to_addr' => 'To',
'msgnum' => 'Message',
unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
my $msgnum = $cgi->param('msgnum');
+
+# XXX This attempt to set a default message isn't working, not sure why
+# $msgnum gets set correctly, but isn't selected in the popup window...fix later
+
unless ($msgnum) {
my ($msg_template) = qsearch('msg_template',{ msgname => 'System log' });
# doesn't seem worth having a config just for the default selected template
'$payinfo' => 'Card/account# (masked)',
'$payinfo_end' => 'Card/account last 4 digits',
],
+ 'system_log' => [
+ '$loglevel' => 'Log event severity level',
+ '$logcontext' => 'Log event context',
+ '$logmessage' => 'Log event message text'
+ ],
);
tie my %sections, 'Tie::IxHash', (
'svc_domain'=> 'Domain service fields',
'svc_phone' => 'Phone service fields',
'svc_broadband' => 'Broadband service fields',
+'system_log' => 'System log fields',
);
my $widget = new HTML::Widgets::SelectLayers(
% } else {
<H1>Log email condition deleted</H1>
<SCRIPT>
-window.top.location.reload();
+window.top.location = "<% $fsurl %>browse/log_email.html";
</SCRIPT>
% }
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
-my $logemailnum = $cgi->param('logemailnum');
-$logemailnum =~ /^\d+$/ or die "bad logemailnum '$logemailnum'";
-my $log_email = FS::log_email->by_key($logemailnum)
- or die "logemailnum '$logemailnum' not found";
-my $error = $log_email->delete;
+ my $error;
+ my $logemailnum = $cgi->param('logemailnum');
+ if ( $logemailnum && $logemailnum =~ /^\d+$/ ) {
+ if ( my $log_email = FS::log_email->by_key( $logemailnum ) ) {
+ $error = $log_email->delete;
+ }
+ }
</%init>
# Standard discount, not a waived setup fee
my $discount = qsearchs('discount',{
discountnum => $_[0]->discountnum
- });
- return $discount->description;
+ }) || return 'Bad discountnum '.$_[0]->pkgdiscountnum;
+ return encode_entities $discount->description;
} else {
return 'Waive setup fee';
}
my $discount = qsearchs('discount',{
discountnum => $_[0]->discountnum
});
- return $discount->classname;
+ return encode_entities $discount->classname;
} else {
return 'n/a';
}
<DIV STYLE="display:inline-block; vertical-align:baseline">
<INPUT TYPE="radio" NAME="all_tags" VALUE="0" CHECKED> Any of these
<BR>
- <INPUT TYPE="radio" NAME="all_tags" VALUE="1"> All of these
+ <INPUT TYPE="radio" NAME="all_tags" VALUE="all"> All of these
+ <BR>
+ <INPUT TYPE="radio" NAME="all_tags" VALUE="none"> None of these
</DIV>
</TD>
</TR>
% foreach my $contact ( $prospect_main->contact ) {
<TR>
- <TD ALIGN="right"><% $contact->contact_classname %> Contact</TD>
- <TD BGCOLOR="#FFFFFF"><% $contact->line %></TD>
+ <TD ALIGN="right"><% $contact->contact_classname |h %> Contact</TD>
+ <TD BGCOLOR="#FFFFFF"><% $contact->line |h %></TD>
</TR>
%}