unless $svc_acct->check_password($p->{'old_password'});
}
- $svc_acct->_password($p->{'new_password'});
+ $svc_acct->set_password($p->{'new_password'});
my $error = $svc_acct->replace();
my($label, $value) = $svc_acct->cust_svc->label;
my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $svcnum } )
or return { 'error' => "Service not found" };
- $svc_acct->_password($p->{'new_password'});
+ $svc_acct->set_password($p->{'new_password'});
my $error = $svc_acct->replace();
my($label, $value) = $svc_acct->cust_svc->label;
'routername', 'varchar', '', $char_d, '', '',
'svcnum', 'int', 'NULL', '', '', '',
'agentnum', 'int', 'NULL', '', '', '',
- 'auto_addr', 'char', 'NULL', 1, '', '',
+ 'manual_addr', 'char', 'NULL', 1, '', '',
],
'primary_key' => 'routernum',
'unique' => [],
=item svcnum - Link to customer service (see L<FS::cust_svc>)
-=item freesidestatus - NULL, processing-tiered, rated, done
+=item freesidestatus - NULL, processing-tiered, rated, done, skipped, no-charge, failed
=item freesiderewritestatus - NULL, done, skipped
);
if ( $reason ) {
warn "not charging for CDR ($reason)\n" if $DEBUG;
- return $self->set_status_and_rated_price( 'rated',
+ return $self->set_status_and_rated_price( 'skipped',
0,
$opt{'svcnum'},
);
my $cdr = shift;
my $object = $self->{inbound} ? $cdr->cdr_termination(1) : $cdr;
my $sec = $object->rated_seconds if $object;
+ $sec ||= 0;
# XXX termination objects don't have rated_granularity so this may
# result in inbound CDRs being displayed as min/sec when they shouldn't.
# Should probably fix this.
}
}
-=item targets
+=item targets OPTIONS
Returns all objects (of type C<FS::eventtable>, for this object's
C<eventtable>) eligible for processing under this event, as of right now.
sub targets {
my $self = shift;
- my $time = time; # $opt{'time'}?
+ my %opt = @_;
+ my $time = $opt{'time'} || time;
my $eventpart = $self->eventpart;
$eventpart =~ /^\d+$/ or die "bad eventpart $eventpart";
});
my @tested_objects;
foreach my $object ( @objects ) {
- my $cust_event = $self->new_cust_event($object, 'time' => $time);
- next unless $cust_event->test_conditions;
+ my $cust_event = $self->new_cust_event($object);
+ next unless $cust_event->test_conditions('time' => $time);
$object->set('cust_event', $cust_event);
push @tested_objects, $object;
--- /dev/null
+package FS::part_event::Condition::signupdate_day;
+
+use strict;
+use Tie::IxHash;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ "Customer signed up on the same day of month as today";
+}
+
+sub option_fields {
+ (
+ 'delay' => { label => 'Delay additional days',
+ type => 'text',
+ value => '0',
+ },
+ );
+}
+
+sub condition {
+ my( $self, $object, %opt ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my ($today) = (localtime($opt{'time'}))[3];
+
+ my $delay = $self->option('delay') || 0;
+ my $signupday = ((localtime($cust_main->signupdate + $delay * 86400))[3] - 1)
+ % 28 + 1;
+
+ $today == $signupday;
+}
+
+sub condition_sql {
+ my( $class, $table, %opt ) = @_;
+ my $mday;
+ if ( $opt{'driver_name'} eq 'Pg' ) {
+ $mday = sub{ "EXTRACT( DAY FROM TO_TIMESTAMP($_[0]) )::INTEGER" };
+ }
+ elsif ( $opt{'driver_name'} eq 'mysql' ) {
+ $mday = sub{ "DAY( FROM_UNIXTIME($_[0]) )" };
+ }
+ else {
+ return 'true';
+ }
+
+ my $delay = $class->condition_sql_option_integer('delay',
+ $opt{'driver_name'}); # returns 0 for null
+ $mday->($opt{'time'}) . ' = '.
+ '(' . $mday->("cust_main.signupdate + $delay * 86400") . ' - 1) % 28 + 1';
+}
+
+1;
=item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
-=item auto_addr - flag to automatically assign IP addresses to services
-linked to this router ('Y' or null).
+=item manual_addr - set to 'Y' to allow services linked to this router
+to have any IP address, rather than one in an address block belonging
+to the router.
=back
my $error =
$self->ut_numbern('routernum')
|| $self->ut_text('routername')
- || $self->ut_enum('auto_addr', [ '', 'Y' ])
+ || $self->ut_enum('manual_addr', [ '', 'Y' ])
|| $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
;
return $error if $error;
sub auto_addr_block {
my $self = shift;
- return () if !$self->auto_addr;
+ return () if $self->manual_addr;
return qsearch('addr_block', { routernum => $self->routernum,
manual_flag => '' });
}
if ( $self->_password_encoding eq 'ldap' ) {
- my $auth = from_rfc2307 Authen::Passphrase $self->_password;
+ $password =~ s/^{PLAIN}/{CLEARTEXT}/;
+ my $auth = from_rfc2307 Authen::Passphrase $password;
return $auth->match($check_password);
} elsif ( $self->_password_encoding eq 'crypt' ) {
sub table { 'svc_broadband'; }
-sub table_dupcheck_fields { ( 'mac_addr' ); }
+sub table_dupcheck_fields { ( 'ip_addr', 'mac_addr' ); }
=item search HASHREF
}
my $agentnum = $cust_pkg->cust_main->agentnum if $cust_pkg;
- if ($self->routernum) {
+ if ( $conf->exists('auto_router') and $self->ip_addr and !$self->routernum ) {
+ # assign_router is guaranteed to provide a router that's legal
+ # for this agent and svcpart
+ my $error = $self->_check_ip_addr || $self->assign_router;
+ return $error if $error;
+ }
+ elsif ($self->routernum) {
return "Router ".$self->routernum." does not provide this service"
unless qsearchs('part_svc_router', {
svcpart => $svcpart,
return "Router ".$self->routernum." does not serve this customer"
if $router->agentnum and $router->agentnum != $agentnum;
- if ( $router->auto_addr ) {
+ if ( $router->manual_addr ) {
+ $self->blocknum('');
+ }
+ else {
my $addr_block = $self->addr_block;
unless ( $addr_block and $addr_block->manual_flag ) {
my $error = $self->assign_ip_addr;
return $error if $error;
}
}
- else {
- $self->blocknum('');
- }
+
+ my $error = $self->_check_ip_addr;
+ return $error if $error;
} # if $self->routernum
if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) {
}
}
- $error = $self->_check_ip_addr;
- return $error if $error;
-
$self->SUPER::check;
}
=item assign_ip_addr
-Assign an address block matching the selected router, and the selected block
+Assign an IP address matching the selected router, and the selected block
if there is one.
=cut
else {
return '';
}
+#warn "assigning ip address in blocks\n".join("\n",map{$_->cidr} @blocks)."\n";
foreach my $block ( @blocks ) {
if ( $self->ip_addr and $block->NetAddr->contains($self->NetAddr) ) {
}
}
+=item assign_router
+
+Assign an address block and router matching the selected IP address.
+Does nothing if IP address is null.
+
+=cut
+
+sub assign_router {
+ my $self = shift;
+ return '' if !$self->ip_addr;
+ #warn "assigning router/block for ".$self->ip_addr."\n";
+ foreach my $router ($self->allowed_routers) {
+ foreach my $block ($router->addr_block) {
+ if ( $block->NetAddr->contains($self->NetAddr) ) {
+ $self->blocknum($block->blocknum);
+ $self->routernum($block->routernum);
+ return '';
+ }
+ }
+ }
+ return $self->ip_addr.' is not in an allowed block.';
+}
+
sub _check_ip_addr {
my $self = shift;
return '' if $conf->exists('svc_broadband-allow_null_ip_addr');
return 'IP address required';
}
+ else {
+ return 'Cannot parse address: '.$self->ip_addr unless $self->NetAddr;
+ }
# if (my $dup = qsearchs('svc_broadband', {
# ip_addr => $self->ip_addr,
# svcnum => {op=>'!=', value => $self->svcnum}
sub _check_duplicate {
my $self = shift;
- return "MAC already in use"
- if ( $self->mac_addr &&
- scalar( qsearch( 'svc_broadband', { 'mac_addr', $self->mac_addr } ) )
- );
+ $self->lock_table;
+
+ my @dup;
+ @dup = $self->find_duplicates('global', 'ip_addr');
+ if ( @dup ) {
+ return "IP address in use (svcnum ".$dup[0]->svcnum.")";
+ }
+ @dup = $self->find_duplicates('global', 'mac_addr');
+ if ( @dup ) {
+ return "MAC address in use (svcnum ".$dup[0]->svcnum.")";
+ }
'';
}
sub allowed_routers {
my $self = shift;
my $svcpart = $self->svcnum ? $self->cust_svc->svcpart : $self->svcpart;
- map { $_->router } qsearch('part_svc_router',
+ my @r = map { $_->router } qsearch('part_svc_router',
{ svcpart => $self->cust_svc->svcpart });
+ if ( $self->cust_main ) {
+ my $agentnum = $self->cust_main->agentnum;
+ return grep { !$_->agentnum or $_->agentnum == $agentnum } @r;
+ }
+ else {
+ return @r;
+ }
}
=back
if ( $conf->exists('svc_hardware-check_mac_addr') ) {
$hw_addr = uc($hw_addr);
$hw_addr =~ /^[0-9A-F]{12}$/
- or return "Illegal (MAC address) ".$self->getfield('hw_addr');
+ or return "Illegal (MAC address) '".$self->getfield('hw_addr')."'";
}
$self->setfield('hw_addr', $hw_addr);
my $event_sub = sub {
my $part_event = shift;
my $onclick = include('/elements/popup_link_onclick.html',
- action => $p.'view/part_event-targets.html?'.$part_event->eventpart,
+ action => $p.'view/part_event-targets.html?eventpart='.
+ $part_event->eventpart,
actionlabel => 'Event query - '.$part_event->event,
width => 650,
height => 420,
shift->addr_block
);
},
- sub { shift->auto_addr ? 'Automatic' : 'Manual' },
+ sub { shift->manual_addr ? 'Manual' : 'Automatic' },
sub { 'Delete' },
],
'links' => [ [ "${p2}edit/router.cgi?", 'routernum' ],
Chris Cappuccio<BR>
Rebecca Cardennis<BR>
Shane Chrisp<BR>
+Kendall Conrad<BR>
Luke Crawford<BR>
Brad Dameron<BR>
Dave Denney<BR>
Contains code derived from HTML::GoogleMapsV3 by David Peters, licensed under
the same terms as Perl (GPL/Artistic).
+<P>
+Contains the Masked Input JavaScript library by Kendall Conrad, licensed under
+a <a href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons
+Attribution-ShareAlike 3.0 United States</a> license.
+
<!-- artwork -->
<P>
sub precheck {
my $cgi = shift;
+ if ( !defined($cgi->param('ip_addr')) ) {
+ $cgi->param('ip_addr', $cgi->param('prev_ip_addr') || '');
+ }
$cgi->param("usergroup", [ $cgi->param('usergroup') ]);
''
}
'routername' => 'Name',
'svc_part' => 'Service',
'agentnum' => 'Agent',
- 'auto_addr' => 'Assign IP addresses automatically',
+ 'manual_addr' => 'Assign IP addresses manually',
},
'fields' => [
{ 'field'=>'routername', 'type'=>'text', 'size'=>32 },
{ 'field'=>'agentnum', 'type'=>'select-agent' },
{ 'field'=>'svcnum', 'type'=>'hidden' },
- { 'field'=>'auto_addr','type'=>'checkbox','value'=>'Y'},
+ { 'field'=>'manual_addr','type'=>'checkbox','value'=>'Y'},
],
'error_callback' => $callback,
'edit_callback' => $callback,
my @fields = (
qw( description speed_down speed_up ),
{ field=>'sectornum', type=>'select-tower_sector', },
- { field=>'routernum', type=>'select-router_block_ip', },
- qw( mac_addr latitude longitude altitude vlan_profile
- performance_profile authkey plan_id ),
+ { field=>'routernum', type=>'select-router_block_ip' },
+ { field=>'mac_addr' , type=>'input-mac_addr' },
+ qw( latitude longitude altitude vlan_profile
+ performance_profile authkey plan_id )
);
if ( $conf->exists('svc_broadband-radius') ) {
}
}
-my $fixedblock = '';
-
my $part_svc;
my $svc_edit_callback = sub {
$part_svc = $part_svc_x; #for field_callback to use
- $opt->{'labels'}{'block_label'} = 'Block';
-
my ($nas_export) = $part_svc->part_export('broadband_nas');
#can we assume there's only one of these per part_svc?
if ( $nas_export ) {
? 'fixed'
: 'hidden';
$fieldref->{'value'} = $columndef->columnvalue;
- $fixedblock = $fieldref->{value}
- if $fieldref->{field} eq 'blocknum';
-
+
if ( $fieldref->{field} eq 'usergroup' ) {
$fieldref->{'formatted_value'} =
[ $object->radius_groups('long_description') ];
}
}
- if ($object->svcnum) {
-
- $fieldref->{type} = 'hidden'
- if $fieldref->{field} eq 'blocknum';
-
- $fieldref->{value} = $object->addr_block->label
- if $fieldref->{field} eq 'block_label' && $object->addr_block;
-
- } else {
-
- if ($fieldref->{field} eq 'block_label') {
- if ($fixedblock && $object->addr_block) {
- $object->blocknum($fixedblock);
- $fieldref->{value} = $object->addr_block->label;
- }else{
- $fieldref->{type} = 'hidden';
- }
- }
-
- if ($fieldref->{field} eq 'blocknum') {
- if ( $fixedblock or $conf->exists('auto_router') ) {
- $fieldref->{type} = 'hidden';
- $fieldref->{value} = $fixedblock;
- return;
- }
-
- my $cust_pkg = qsearchs( 'cust_pkg', {pkgnum => $cgi->param('pkgnum')} );
- die "No cust_pkg entry!" unless $cust_pkg;
-
- $object->svcpart($part_svc->svcpart);
- my @addr_block =
- grep { ! $_->agentnum
- || $cust_pkg->cust_main->agentnum == $_->agentnum
- && $FS::CurrentUser::CurrentUser->agentnum($_->agentnum)
- }
- map { $_->addr_block } $object->allowed_routers;
- my @options = map { $_->blocknum }
- sort { $a->label cmp $b->label } @addr_block;
- my %option_labels = map { ( $_->blocknum => $_->label ) } @addr_block;
- $fieldref->{type} = 'select';
- $fieldref->{options} = \@options;
- $fieldref->{labels} = \%option_labels;
- }
-
- }
};
</%init>
},
{
field => 'hw_addr',
- type => 'text',
+ type => $conf->exists('svc_hardware-check_mac_addr') ?
+ 'input-mac_addr' : 'text',
label => 'Hardware address',
},
{
--- /dev/null
+/***********************************************************************
+ Masked Input version 1.1
+************************************************************************
+Author: Kendall Conrad
+Home page: http://www.angelwatt.com/coding/masked_input.php
+Created: 2008-12-16
+Modified: 2010-04-14
+Description:
+License: This work is licensed under a Creative Commons Attribution-Share Alike
+ 3.0 United States License http://creativecommons.org/licenses/by-sa/3.0/us/
+
+Argument pieces:
+- elm: [req] text input node to apply the mask on
+- format: [req] string format for the mask
+- allowed: [opt, '0123456789'] string with chars allowed to be typed
+- sep: [opt, '\/:-'] string of char(s) used as separators in mask
+- typeon: [opt, '_YMDhms'] string of chars in mask that can be typed on
+- onbadkey: [opt, null] function to run when user types a unallowed key
+- badkeywait: [opt, 0] used with onbadkey. Indicates how long (in ms) to lock
+ text input for onbadkey function to run
+***********************************************************************/
+function MaskedInput(args)
+{
+ if (args['elm'] === null || args['format'] === null) { return false; }
+ var el = args['elm'],
+ format = args['format'],
+ allowed = args['allowed'] || '0123456789',
+ sep = args['separator'] || '\/:-',
+ open = args['typeon'] || '_YMDhms',
+ onbadkey = args['onbadkey'] || function(){},
+ badwait = args['badkeywait'] || 0;
+
+ var locked = false, hold = 0;
+ el.value = format;
+ // Assign events
+ el.onkeydown = KeyHandlerDown; //
+ el.onkeypress = KeyHandlerPress; // add event handlers to element
+ el.onkeyup = KeyHandlerUp; //
+
+ function GetKey(code)
+ {
+ code = code || window.event, ch = '';
+ var keyCode = code.which, evt = code.type;
+ if (keyCode == null) { keyCode = code.keyCode; }
+ if (keyCode === null) { return ''; } // no key, no play
+ // deal with special keys
+ switch (keyCode) {
+ case 8: ch = 'bksp'; break;
+ case 46: // handle del and . both being 46
+ ch = (evt == 'keydown') ? 'del' : '.'; break;
+ case 16: ch = 'shift'; break;//shift
+ case 0:/*CRAP*/ case 9:/*TAB*/ case 13:/*ENTER*/
+ ch = 'etc'; break;
+ case 37: case 38: case 39: case 40: // arrow keys
+ ch = (!code.shiftKey &&
+ (code.charCode != 39 && code.charCode !== undefined)) ?
+ 'etc' : String.fromCharCode(keyCode);
+ break;
+ // default to thinking it's a character or digit
+ default: ch = String.fromCharCode(keyCode);
+ }
+ return ch;
+ }
+ function KeyHandlerDown(e)
+ {
+ e = e || event;
+ if (locked) { return false; }
+ var key = GetKey(e);
+ if (el.value == '') { el.value = format; SetTextCursor(el,0); }
+ // Only do update for bksp del
+ if (key == 'bksp' || key == 'del') { Update(key); return false; }
+ else if (key == 'etc' || key == 'shift') { return true; }
+ else { return true; }
+ }
+ function KeyHandlerPress(e)
+ {
+ e = e || event;
+ if (locked) { return false; }
+ var key = GetKey(e);
+ // Check if modifier key is being pressed; command
+ if (key=='etc' || e.metaKey || e.ctrlKey || e.altKey) { return true; }
+ if (key != 'bksp' && key != 'del' && key != 'etc' && key != 'shift') {
+ if (!GoodOnes(key)) { return false; }
+ return Update(key);
+ }
+ else { return false; }
+ }
+ function KeyHandlerUp(e) { hold = 0; }
+ function Update(key)
+ {
+ var p = GetTextCursor(el), c = el.value, val = '';
+ // Handle keys now
+ switch (true) {
+ case (allowed.indexOf(key) != -1):
+ if (++p > format.length) { return false; } // if text csor at end
+ // Handle cases where user places csor before separator
+ while (sep.indexOf(c.charAt(p-1)) != -1 && p <= format.length) { p++; }
+ val = c.substr(0, p-1) + key + c.substr(p);
+ // Move csor up a spot if next char is a separator char
+ if (allowed.indexOf(c.charAt(p)) == -1
+ && open.indexOf(c.charAt(p)) == -1) { p++; }
+ break;
+ case (key=='bksp'): // backspace
+ if (--p < 0) return false; // at start of field
+ // If previous char is a separator, move a little more
+ while (allowed.indexOf(c.charAt(p)) == -1
+ && open.indexOf(c.charAt(p)) == -1
+ && p > 1) { p--; }
+ val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
+ break;
+ case (key=='del'): // forward delete
+ if (p >= c.length) { return false; } // at end of field
+ // If next char is a separator and not the end of the text field
+ while (sep.indexOf(c.charAt(p)) != -1
+ && c.charAt(p) != '') { p++; }
+ val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
+ p++; // Move position forward
+ break;
+ case (key=='etc'): return true; // Catch other allowed chars
+ default: return false; // Ignore the rest
+ }
+ el.value = ''; // blank it first (Firefox issue)
+ el.value = val; // put updated value back in
+ SetTextCursor(el, p); // Set the text cursor
+ return false;
+ }
+ function GetTextCursor(node)
+ {
+ try {
+ if (node.selectionStart >= 0) { return node.selectionStart; }
+ else if (document.selection) {// IE
+ var ntxt = node.value; // getting starting text
+ var rng = document.selection.createRange();
+ rng.text = '|%|';
+ var start = node.value.indexOf('|%|');
+ rng.moveStart('character', -3);
+ rng.text = '';
+ // put starting text back in,
+ // fixes issue if all text was highlighted
+ node.value = ntxt;
+ return start;
+ } return -1;
+ } catch(e) { return false; }
+ }
+ function SetTextCursor(node, pos)
+ {
+ try {
+ if (node.selectionStart) {
+ node.focus();
+ node.setSelectionRange(pos,pos);
+ }
+ else if (node.createTextRange) { // IE
+ var rng = node.createTextRange();
+ rng.move('character', pos);
+ rng.select();
+ }
+ } catch(e) { return false; }
+ }
+ function GoodOnes(k)
+ {
+ if (allowed.indexOf(k) == -1 && k!='bksp' && k!='del' && k!='etc') {
+ var p = GetTextCursor(el); // Need to ensure cursor position not lost
+ locked = true; onbadkey();
+ // Hold lock long enough for onbadkey function to run
+ setTimeout(function(){locked=false; SetTextCursor(el,p);}, badwait);
+ return false;
+ } return true;
+ }
+ function resetField() {
+ el.value = format;
+ }
+ function setAllowed(a) {
+ allowed = a;
+ resetField();
+ }
+ function setFormat(f) {
+ format = f;
+ resetField();
+ }
+ function setSeparator(s) {
+ sep = s;
+ resetField();
+ }
+ function setTypeon(t) {
+ open = t;
+ resetField();
+ }
+ return {
+ resetField:resetField,
+ setAllowed:setAllowed,
+ setFormat:setFormat,
+ setSeparator:setSeparator,
+ setTypeon:setTypeon
+ }
+}
--- /dev/null
+<& /elements/tr-input-mask.html,
+ format => '__:__:__:__:__:__',
+ allowed => '0123456789ABCDEFabcdef',
+ %opt,
+&>
+<%init>
+my %opt = @_;
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
+$value =~ s/\W//g;
+$opt{curr_value} = join(':', $value =~ /../g);
+</%init>
--- /dev/null
+% if ( !$init ) {
+<script type="text/javascript" src="<%$p%>elements/masked_input_1.1.js">
+</script>
+% $init++;
+% }
+<& /elements/tr-input-text.html, id => $id, @_ &>
+<script type="text/javascript">
+MaskedInput({
+ elm: document.getElementById('<%$id%>'),
+ format: '<% $opt{format} %>',
+ <% $opt{allowed} ? "allowed: '$opt{allowed}'," : '' %>
+ <% $opt{typeon} ? "typeon: '$opt{typeon}'," : '' %>
+});
+document.getElementById('<%$id%>').value = <% $value |js_string %>;
+</script>
+<%shared>
+my $init = 0;
+</%shared>
+<%init>
+my %opt = @_;
+# must have a DOM id
+my $id = $opt{id} || sprintf('input%04d',int(rand(10000)));
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value} || '';
+</%init>
+<%doc>
+Set up a text input field with input masking.
+
+<& /elements/tr-input-mask.html,
+ format => '____-__-__',
+ #typeon => '_YMDhms', # which characters in the format represent blanks
+ #allowed => '0123456789', # characters allowed in the blanks
+ ... all other options as for tr-input-text.html
+&>
+
+Note that the value sent on form submission will contain the mask
+separators, and if value/curr_value is passed, it should also be
+formatted to fit the mask.
+
+Uses masked_input_1.1.js by Kendall Conrad, available under a Creative Commons
+Attribution-ShareAlike license.
+</%doc>
<script type="text/javascript">
-var auto_addr_routernum = <% encode_json(\%auto_addr_routernum) %>;
-function hide_if_auto_addr(obj, i) {
+var manual_addr_routernum = <% encode_json(\%manual_addr_routernum) %>;
+var ip_addr_curr_value = <% $opt{'ip_addr'} |js_string %>;
+function lock_ip_addr(obj, i) {
var routernum = obj.value;
var select_blocknum = document.getElementsByName('blocknum')[0];
- var label_auto_addr = document.getElementById('label_auto_addr');
var input_ip_addr = document.getElementById('input_ip_addr');
- var auto = ( auto_addr_routernum[routernum] == 'Y' );
- select_blocknum.style.display = auto ? '' : 'none';
- label_auto_addr.style.display = auto ? '' : 'none';
- input_ip_addr.style.display = !auto ? '' : 'none';
+ if ( manual_addr_routernum[routernum] == 'Y' ) {
+%# enable ip_addr, default it to its previous value, and hide block selection
+ select_blocknum.style.display = 'none';
+ input_ip_addr.value = ip_addr_curr_value;
+ input_ip_addr.disabled = false;
+ }
+ else {
+%# the reverse
+ select_blocknum.style.display = '';
+ input_ip_addr.disabled = true;
+ input_ip_addr.value = '(automatic)';
+ }
}
</script>
<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router') &>
records => \@routers,
name_col => 'routername',
value_col => 'routernum',
- onchange => 'hide_if_auto_addr',
+ onchange => 'lock_ip_addr',
curr_value=> $opt{'routernum'},
},
{
% }
% else {
<input type="text" id="input_ip_addr" name="ip_addr"
- style="display:none" value="<% $opt{'ip_addr'} |h%>">
+ value="<% $opt{'ip_addr'} |h%>">
% }
- <span id="label_auto_addr"><% $opt{'ip_addr'} || '' %>
- <i>(automatic)</i></span>
</td> </tr>
+<input type="hidden" name="prev_ip_addr" value="<% $opt{'ip_addr'} |h%>">
<script type="text/javascript">
-hide_if_auto_addr(document.getElementsByName('routernum')[0],0);
+lock_ip_addr(document.getElementsByName('routernum')[0],0);
</script>
<%init>
+
my %opt = @_;
my @routers;
+my $conf = FS::Conf->new;
my $svc_x = $opt{'object'};
if ( $svc_x ) {
@routers = qsearch('router', {});
}
-my %auto_addr_routernum = map { $_->routernum, $_->auto_addr } @routers;
+my %manual_addr_routernum = map { $_->routernum, $_->manual_addr } @routers;
+
+if ( $conf->exists('auto_router') ) {
+ # Then show an "(automatic)" router, with no blocks. manual_addr is on
+ # so that the ip_addr field will be unlocked.
+ unshift @routers, FS::router->new({
+ 'routernum' => '',
+ 'routername' => '(automatic)',
+ 'manual_addr' => 'Y',
+ });
+ $manual_addr_routernum{''} = 'Y';
+}
+
</%init>
'title' => 'Event query - '.$part_event->event,
}
&>
+<FORM STYLE="display:inline" ACTION=<%$cgi->url%> METHOD="GET">
+When event is run on <& /elements/input-date-field.html, {
+ 'name' => 'date',
+ 'value' => $time,
+ 'format' => FS::Conf->new->config('date_format') || '%m/%d/%Y',
+} &>
+<INPUT TYPE="hidden" NAME="eventpart" VALUE="<%$eventpart%>">
+<INPUT TYPE="submit" VALUE="Refresh">
+</FORM>
+<BR><BR>
% if ( $objects > 0 ) {
<% emt("[quant,_1,$label]", $objects) %>
% if ( $part_event->eventtable ne 'cust_main' ) {
% my @rowcolors = ('ffffff','eeeeee');
% my $row = 0;
- <TR style="background-color:#<% $rowcolors[$row++ % 2] %>">
% foreach my $object (@targets) {
+ <TR style="background-color:#<% $rowcolors[$row++ % 2] %>">
% # now works for all eventtables, including cust_pkg
% my $link = $p . 'view/' . $part_event->eventtable . '.cgi?' .
% $object->$pkey;
unless $curuser->access_right('Edit billing events')
|| $curuser->access_right('Edit global billing events');
-my ($eventpart) = $cgi->keywords;
+my ($eventpart) = $cgi->param('eventpart');
$eventpart =~ /^\d+$/ or die 'illegal eventpart';
+my $time = parse_datetime($cgi->param('date')) || time;
+
my $part_event = FS::part_event->by_key($eventpart)
or die "Event definition $eventpart not found.\n";
-my @targets = $part_event->targets;
+my @targets = $part_event->targets('time' => $time);
my $total = @targets;
# in imitation of search/elements/search-html.html
#my %labels = ();
$labels{'description'} = emt('Description');
-$labels{'router'} = emt('Router');
$labels{'speed_down'} = emt('Download Speed');
$labels{'speed_up'} = emt('Upload Speed');
$labels{'ip_addr'} = emt('IP Address');
'speed_up',
{ field => 'ip_addr', value => \&ip_addr },
{ field => 'sectornum', value => \§ornum },
- 'mac_addr',
+ { field => 'mac_addr', value => \&mac_addr },
#'latitude',
#'longitude',
{ field => 'coordinates', value => \&coordinates },
$out;
}
+sub mac_addr {
+ my $svc = shift;
+ join(':', $svc->mac_addr =~ /../g);
+}
+
sub usergroup {
my $svc = shift;
my $usergroup = $svc->usergroup;
%>
<%init>
+my $conf = new FS::Conf;
my $fields = FS::svc_hardware->table_info->{'fields'};
my %labels = map { $_ => ( ref($fields->{$_})
? $fields->{$_}{'label'}
type => 'text',
value => sub { encode_entities($_[0]->note) }
};
-my @fields = ($model, qw( serial hw_addr ip_addr smartcard ), $status, $note );
+my $hw_addr ={ field => 'hw_addr',
+ type => 'text',
+ value => sub {
+ my $hw_addr = $_[0]->hw_addr;
+ $conf->exists('svc_hardware-check_mac_addr') ?
+ join(':', $hw_addr =~ /../g) : $hw_addr
+ },
+ };
+
+my @fields = (
+ $model,
+ 'serial',
+ $hw_addr,
+ 'ip_addr',
+ 'smartcard',
+ $status,
+ $note,
+);
</%init>