diff options
author | mark <mark> | 2010-07-01 01:53:50 +0000 |
---|---|---|
committer | mark <mark> | 2010-07-01 01:53:50 +0000 |
commit | de3bf373e97f6875e09fc17d99068a2716bf3b2d (patch) | |
tree | bf5d4908cb57519a99cb8db0266f40d671f9e170 /httemplate | |
parent | 6643a544400f5bed64b75720fdaccb41f01d1e79 (diff) |
voip_cdr call rating by day and time, RT#4763
Diffstat (limited to 'httemplate')
-rw-r--r-- | httemplate/browse/rate.cgi | 2 | ||||
-rw-r--r-- | httemplate/browse/rate_detail.html | 205 | ||||
-rw-r--r-- | httemplate/browse/rate_time.html | 48 | ||||
-rw-r--r-- | httemplate/edit/process/rate_time.cgi | 94 | ||||
-rw-r--r-- | httemplate/edit/rate_detail.html | 9 | ||||
-rw-r--r-- | httemplate/edit/rate_time.cgi | 44 | ||||
-rw-r--r-- | httemplate/elements/auto-table.html | 146 | ||||
-rwxr-xr-x | httemplate/misc/delete-rate_detail.html | 20 |
8 files changed, 511 insertions, 57 deletions
diff --git a/httemplate/browse/rate.cgi b/httemplate/browse/rate.cgi index 02d670fbd..428158a6d 100644 --- a/httemplate/browse/rate.cgi +++ b/httemplate/browse/rate.cgi @@ -2,6 +2,8 @@ 'title' => 'Rate plans', 'menubar' => [ 'Regions and Prefixes' => $p.'browse/rate_region.html', + 'Time Periods' => + $p.'browse/rate_time.html', ], 'html_init' => $html_init, 'name' => 'rate plans', diff --git a/httemplate/browse/rate_detail.html b/httemplate/browse/rate_detail.html index 3371926b4..faaec2c38 100644 --- a/httemplate/browse/rate_detail.html +++ b/httemplate/browse/rate_detail.html @@ -1,39 +1,53 @@ -<% include( 'elements/browse.html', - 'title' => $title, - 'name_singular' => 'rate', - 'html_init' => $html_init, - 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ], - 'query' => { - 'table' => 'rate_detail', - 'addl_from' => $join, - 'hashref' => { 'ratenum' => $ratenum }, - 'extra_sql' => $where, - }, - 'count_query' => "SELECT COUNT(*) FROM rate_detail $join". - " WHERE ratenum = $ratenum $where", - 'header' => [ - 'Region', - 'Prefix(es)', - 'Included<BR>minutes', - 'Connection<BR>charge', - 'Charge per<BR>minute', - 'Granularity', - 'Usage class', - ], - 'fields' => [ - 'regionname', - sub { shift->dest_region->prefixes_short }, - sub { shift->min_included. $edit_hint }, - $conn_charge_sub, - sub { $money_char. shift->min_charge. $edit_hint }, - sub { $granularity{ shift->sec_granularity } }, - 'classname', - ], - 'links' => [ '', '', $edit_link, $edit_link, '', '' ], - 'link_onclicks' => [ '', '', $edit_onclick, $edit_onclick, '', '' ], - 'align' => 'llrrcc', - ) -%> +<% include('/elements/init_overlib.html') %> +<% include('/elements/header.html',$title) %> +<% include('/elements/menubar.html',@menubar) %> +<BR><BR> +<% include('/elements/table-grid.html') %> +<TR> +% my $col = 0; +% foreach (@header) { +% my $hlink = $hlinks[$col]; + <TH CLASS = "grid", + BGCOLOR = "#cccccc"> + <% $hlink ? qq!<A HREF="$hlink">$_</A>! : $_ %> + </TH> +% $col++; +% } #foreach @header +</TR><TR> +% my $row = 0; +% foreach my $region (@rate_region) { +% $col = 0; +% foreach ($region->regionname, $region->prefixes_short) { + <TD> + <A HREF="<% $p.'edit/rate_region.cgi?'.$region->regionnum %>"><% $_ %></A> + </TD> +% } +% foreach my $rate_time (@rate_time, '') { + <TD> +% my $detail = $details[$row][$col]; +% if($detail) { + <TABLE CLASS="inv" STYLE="border:none"> + <TR><TD><% edit_link($detail) %><% $money_char.$detail->min_charge %> + <% $detail->sec_granularity ? ' / minute':' / call' %> + <% $edit_hint %></A> + </TD></TR> + <% granularity_detail($detail) %> + <% min_included_detail($detail) %> + <% conn_charge_detail($detail) %> + <TR><TD><% $rate_time ? delete_link($detail) : '' %></TD></TR> + </TABLE> +% } +% else { #!$detail + <% add_link($ratenum, $region, $rate_time) %> +% } +% $col++; + </TD> +% } # foreach @rate_time +</TR> +% $row++; +% }# foreach @rate_region +<% include('/elements/footer.html') %> + <%once> tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); @@ -42,31 +56,92 @@ tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs(); my $conf = new FS::Conf; my $money_char = $conf->config('money_char') || '$'; -my $join = - ' JOIN rate_region ON ( rate_detail.dest_regionnum = rate_region.regionnum )'; +my @menubar = ( 'Rate plans' => $p.'browse/rate.cgi', + 'Regions and Prefixes' => $p.'browse/rate_region.html', + 'Time Periods' => $p.'browse/rate_time.html', + ); -my $edit_link = [ 'javascript:void(0);', sub { ''; } ]; +sub small { + '<FONT SIZE="-1">'.shift.'</FONT>' +} +my $edit_hint = small('(edit)'); -my $edit_onclick = sub { +sub edit_link { my $rate_detail = shift; my $ratedetailnum = $rate_detail->ratedetailnum; + '<A HREF="javascript:void(0);" onclick="'. include( '/elements/popup_link_onclick.html', 'action' => "${p}edit/rate_detail.html?$ratedetailnum", 'actionlabel' => 'Edit rate', 'height' => 420, #default# 'width' => 540, #default# 'color' => '#333399', - ); -}; -my $edit_hint = ' <FONT SIZE="-1">(edit)</FONT>'; - -my $conn_charge_sub = sub { - my $rate_detail = shift; - #return '' unless $rate_detail->conn_charge > 0 || $rate_detail->conn_sec; - $money_char. $rate_detail->conn_charge. - ($rate_detail->conn_sec ? ' for '.$conn_secs{$rate_detail->conn_sec} : ''). - $edit_hint; -}; + ) . '">' +} + +sub add_link { + my ($ratenum, $region, $rate_time) = @_; + '<A HREF="javascript:void(0);" onclick="'. + include( '/elements/popup_link_onclick.html', + 'action' => "${p}edit/rate_detail.html?ratenum=$ratenum". + ';dest_regionnum='.$region->regionnum. + ($rate_time ? + ';ratetimenum='.$rate_time->ratetimenum : + ''), + 'actionlabel' => 'Add rate', + 'height' => 420, + ).'">'.small('(add)').'</A>' +} + +sub delete_link { + my $rate_detail = shift; + my $ratedetailnum = $rate_detail->ratedetailnum; + my $onclick = include( '/elements/popup_link_onclick.html', + 'action' => "${p}misc/delete-rate_detail.html?$ratedetailnum", + 'actionlabel' => 'Delete rate', + 'width' => 510, + 'height' => 315, + 'frame' => 'top', + ); + $onclick = "if(confirm('Delete this rate?')) { $onclick }"; + qq!<A HREF="javascript:void(0);" onclick="$onclick">!.small('(delete)').'</A>' +} + +sub granularity_detail { + my $rate_detail = shift; + if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) { + '<TR><TD>'. + small('in '.$granularity{$rate_detail->sec_granularity}.' increments'). + '</TD></TR>'; + } + else { '' } +} + +sub min_included_detail { + my $rate_detail = shift; + if($rate_detail->min_included) { + '<TR><TD>'. + small( $rate_detail->min_included . + ($rate_detail->sec_granularity ? + ' minutes included' : + ' calls included') ). + '</TD></TR>' + } + else { '' } +} + +sub conn_charge_detail { + my $rate_detail = shift; + if($rate_detail->conn_charge > 0) { + #return '' unless $rate_detail->conn_charge > 0 || $rate_detail->conn_sec; + '<TR><TD>'. + small( $money_char. $rate_detail->conn_charge. + ' for '.$conn_secs{$rate_detail->conn_sec} + ). + '</TD></TR>' + } + else { '' } +} </%once> <%init> @@ -74,8 +149,6 @@ my $conn_charge_sub = sub { die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); -my $html_init = include('/elements/init_overlib.html'); - $cgi->param('ratenum') =~ /^(\d+)$/ or die "unparsable ratenum"; my $ratenum = $1; my $rate = qsearchs('rate', { 'ratenum' => $ratenum } ) @@ -83,11 +156,10 @@ my $rate = qsearchs('rate', { 'ratenum' => $ratenum } ) my $ratename = $rate->ratename; my $title = "$ratename rates"; -my @where = (); - +my $where; if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) { my $countrycode = $1; - push @where, "0 < ( SELECT COUNT(*) FROM rate_prefix + $where = "WHERE 0 < ( SELECT COUNT(*) FROM rate_prefix WHERE rate_prefix.regionnum = rate_region.regionnum AND countrycode = '$countrycode' ) @@ -95,6 +167,25 @@ if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) { $title .= " for +$countrycode"; } -my $where = scalar(@where) ? ' AND '.join(' AND ', @where ) : ''; +my @rate_region = qsearch({ table => 'rate_region', + hashref => {}, + extra_sql => $where, + }); + +my @rate_time = qsearch('rate_time', {}); +my @header = ('Region', 'Prefix(es)', + map( { $_->ratetimename } @rate_time ), + '(default)'); +my @hlinks = map {''} @header; + +my @rtns = ( map( { $_->ratetimenum } @rate_time ), '' ); +my @details; +foreach my $region (@rate_region) { + push @details, [ map { qsearchs('rate_detail', + { 'ratenum' => $ratenum, + 'dest_regionnum' => $region->regionnum, + 'ratetimenum' => $_ } ) or '' + } @rtns ]; +} </%init> diff --git a/httemplate/browse/rate_time.html b/httemplate/browse/rate_time.html new file mode 100644 index 000000000..416ded41f --- /dev/null +++ b/httemplate/browse/rate_time.html @@ -0,0 +1,48 @@ +<% include( 'elements/browse.html', + 'title' => 'Rating Time Periods', + 'name_singular' => 'period', + 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ], + 'html_init' => $html_init, + 'query' => { + 'table' => 'rate_time', + 'order_by' => 'ratetimenum', # lacking anything else + 'hashref' => {}, + }, + 'count_query' => 'SELECT COUNT(*) FROM rate_time', + 'header' => \@header, + 'fields' => \@fields, + 'links' => \@links, + 'align' => \@align, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $edit_url = $p.'edit/rate_time.cgi'; + +my $link = [ "$edit_url?", 'ratetimenum' ]; + +my $html_init = + 'Time periods for VoIP and call billing.<BR><BR>'. + qq(<A HREF="$edit_url"><I>Add a new period</I></A><BR><BR>); + +sub interval { + my $i = shift; + '<TABLE>' + .join('', map { '<TR><TD>'.($_->description)[$i].'</TR></TD>' } + shift->intervals + ) . '</TABLE>'; +} + +# inefficient but readable +my $stime_sub = sub { interval(0,shift) }; +my $etime_sub = sub { interval(1,shift) }; + +my @header = ( '#', 'Period', 'Start', 'End' ); +my @fields = ( 'ratetimenum', 'ratetimename', $stime_sub, $etime_sub ); +my @links = ( ($link) x 2 ); +my @align = ( 'right', 'left', 'left' ); + +</%init> diff --git a/httemplate/edit/process/rate_time.cgi b/httemplate/edit/process/rate_time.cgi new file mode 100644 index 000000000..48ed2739e --- /dev/null +++ b/httemplate/edit/process/rate_time.cgi @@ -0,0 +1,94 @@ +% if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "rate_time.cgi?". $cgi->query_string ) %> +% } else { +<% $cgi->redirect(popurl(3). "browse/rate_time.html" ) %> +% } +%# dumper_html(\%vars, \%old_ints, {$rate_time->intervals}) %> +<%init> +my $error = ''; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +my $ratetimenum = $cgi->param('ratetimenum'); +my $ratetimename = $cgi->param('ratetimename'); +my $delete = $cgi->param('delete'); + +my %vars = $cgi->Vars; +#warn Dumper(\%vars)."\n"; + +my $rate_time; + +my %old_ints; +if( $ratetimenum ) { + # editing + $rate_time = FS::rate_time->by_key($ratetimenum); + + # make a list of existing intervals that will be deleted + foreach ($rate_time->intervals) { + $old_ints{$_->intervalnum} = $_; + } + + if ( $delete ) { + $error = $rate_time->delete; + # intervals will be deleted later + } + elsif( $ratetimename ne $rate_time->ratetimename ) { + # the only case where the rate_time itself must be replaced + $rate_time->ratetimename($ratetimename); + $error = $rate_time->replace; + } +} +else { #!$ratetimenum, adding new + $rate_time = FS::rate_time->new({ ratetimename => $ratetimename }); + $error = $rate_time->insert; + $ratetimenum = $rate_time->ratetimenum; +} + +if(!$delete and !$error) { + foreach my $i (map { /stime(\d+)/ } keys(%vars)) { + my $stime = str2wtime($vars{"stime$i"}); + my $etime = str2wtime($vars{"etime$i"}); + next if !defined($stime) or !defined($etime); + #warn "$i: $stime-$etime"; + # try to avoid needlessly wiping and replacing intervals every + # time this is edited. + if( %old_ints ) { + my $this_int = qsearchs('rate_time_interval', + { ratetimenum => $ratetimenum, + stime => $stime, + etime => $etime, } ); + if($this_int) { + delete $old_ints{$this_int->intervalnum}; + #warn "not deleting $stime-$etime\n"; + next; #$i + } + } + my $new_int = FS::rate_time_interval->new({ ratetimenum => $ratetimenum, + stime => $stime, + etime => $etime, } ); + $error = $new_int->insert; + #warn "inserting $stime-$etime\n"; + last if $error; + } +} +if(!$error) { + foreach (values(%old_ints)) { + $error = $_->delete; + #warn "deleting ".$_->stime.' '.$_->etime."\n"; + last if $error; + } +} + +sub str2wtime { + my %days; + @days{qw(Sun Mon Tue Wed Thu Fri Sat)} = (0..6); + my $str = shift; + my ($d, $h, $m, $s, $ampm) = + ($str =~ /^(\w{3}) (\d{2}):(\d{2}):(\d{2}) (\w{2})$/); + return () if !$d; + $h += 24*$days{$d} + ($ampm eq 'PM' ? 12 : 0); + $m += 60*$h; + $s += 60*$m; + return $s; +} +</%init> diff --git a/httemplate/edit/rate_detail.html b/httemplate/edit/rate_detail.html index 869ace8d4..ec04e4cbb 100644 --- a/httemplate/edit/rate_detail.html +++ b/httemplate/edit/rate_detail.html @@ -5,6 +5,7 @@ 'labels' => { 'ratedetailnum' => 'Rate', #should hide... 'dest_regionname' => 'Region', 'dest_prefixes_short' => 'Prefix(es)', + 'rate_time_name' => 'Time period', 'min_included' => 'Included minutes/calls', 'conn_charge' => 'Connection charge', 'conn_sec' => 'For', @@ -16,8 +17,10 @@ { field=>'ratenum', type=>'hidden', }, { field=>'orig_regionnum', type=>'hidden', }, { field=>'dest_regionnum', type=>'hidden', }, + { field=>'ratetimenum', type=>'hidden', }, { field=>'dest_regionname', type=>'fixed', }, { field=>'dest_prefixes_short', type=>'fixed', }, + { field=>'rate_time_name', type=>'fixed', }, { field=>'min_included', type=>'text', size=>5 }, { field=>'conn_charge', type=>'money', size=>4 }, { field =>'conn_sec', @@ -42,6 +45,12 @@ }, ], + 'new_hashref_callback' => sub { + { ratenum => $cgi->param('ratenum'), + dest_regionnum => $cgi->param('dest_regionnum'), + ratetimenum => $cgi->param('ratetimenum'), + } + }, ) %> <%once> diff --git a/httemplate/edit/rate_time.cgi b/httemplate/edit/rate_time.cgi new file mode 100644 index 000000000..230aef82d --- /dev/null +++ b/httemplate/edit/rate_time.cgi @@ -0,0 +1,44 @@ +<% include("/elements/header.html","$action Time Period", menubar( + 'Rate plans' => "${p}browse/rate.cgi", + ) ) +%> + +<% include('/elements/error.html') %> + +<FORM METHOD="POST" ACTION="<% "${p}edit/process/rate_time.cgi" %>"> +<INPUT TYPE="hidden" NAME="ratetimenum" VALUE="<% $ratetimenum %>"> +<% ntable('#cccccc',2) %> +<TABLE> + <TR> + <TH ALIGN="right">Period name</TH> + <TD><INPUT TYPE="text" NAME="ratetimename" VALUE="<% $rate_time ? $rate_time->ratetimename : '' %>"></TD> + </TR> +</TABLE> +<% include('/elements/auto-table.html', + header => [ 'Start', 'End' ], + fields => [ 'stime', 'etime' ], + size => [ 18, 18 ], + maxl => [ 15, 15 ], + align => [ 'right', 'right' ], + data => \@data, + ) %> +<INPUT TYPE="submit" VALUE="<% $rate_time ? 'Apply changes' : 'Add period'%>"> +</FORM> +<BR> +<A HREF="<% "${p}edit/process/rate_time.cgi?ratetimenum=$ratetimenum;delete=1" %>">Delete this period</A> +<% include('/elements/footer.html') %> + +<%init> +my $ratetimenum = ($cgi->keywords)[0] || ''; +my $action = 'Add'; +my $rate_time; +my @data = (); + +if($ratetimenum) { + $action = 'Edit'; + $rate_time = qsearchs('rate_time', {ratetimenum => $ratetimenum}) + or die "ratetimenum $ratetimenum not found"; + @data = $rate_time->description; +} + +</%init> diff --git a/httemplate/elements/auto-table.html b/httemplate/elements/auto-table.html new file mode 100644 index 000000000..89d6eacb9 --- /dev/null +++ b/httemplate/elements/auto-table.html @@ -0,0 +1,146 @@ +<%doc> + +Example: +<% include('/elements/auto-table.html', + + ### + # required + ### + + 'header' => [ '#', 'Item', 'Amount' ], + 'fields' => [ 'id', 'name', 'amount' ], + + ### + # highly recommended + ### + + 'size' => [ 4, 12, 8 ], + 'maxl' => [ 4, 12, 8 ], + 'align' => [ 'right', 'left', 'right' ], + + ### + # optional + ### + + 'data' => [ [ 1, 'Widget', 25 ], + [ 12, 'Super Widget, 7 ] ], + #or + 'records' => [ qsearch('item', { } ) ], + # or any other array of FS::Record objects + + 'prefix' => 'mytable_', +) %> + +Values will be passed through as "mytable_id1", etc. +</%doc> + +<TABLE ID="<% $prefix %>AutoTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + <TR> +% foreach (@header) { + <TH><% $_ %></TH> +% } + </TR> +% my $row = 0; +% for ( $row = 0; $row < scalar @data; $row++ ) { + <TR> +% my $col = 0; +% for ( $col = 0; $col < scalar @fields; $col++ ) { +% my $id = $prefix . $fields[$col] . $row; + <TD> + <INPUT TYPE = "text" + NAME = "<% $id %>" + ID = "<% $id %>" + SIZE = <% $size[$col] %> + MAXLENGTH = <% $maxl[$col] %> + STYLE = "text-align:<% $align[$col] %>" + VALUE = "<% $data[$row][$col] %>" + onchange = "possiblyAddRow();" + > + </TD> +% } + <TD> + <IMG SRC = "<% "${p}images/cross.png" %>" + ALT = "X" + onclick = "deleteThisRow(this);" + > + </TD> + </TR> +% } +</TABLE> + +<SCRIPT TYPE="text/javascript"> + var <% $prefix %>rownum = <% $row %>; + var <% $prefix %>table = document.getElementById('<% $prefix %>AutoTable'); + + function rownum_of(obj) { + return (obj.parentNode.parentNode.sectionRowIndex); + } + + function possiblyAddRow() { + if ( <% $prefix %>rownum == rownum_of(this) ) { + <% $prefix %>addRow(); + } + } + + function <% $prefix %>addRow() { + var row = <% $prefix %>table.insertRow(<% $prefix %>rownum + 1); +% my $col = 0; +% for( $col = 0; $col < scalar @fields; $col++ ) { +% my $field = $prefix.$fields[$col]; + var <% $field %>_cell = document.createElement('TD'); + var <% $field %>_input = document.createElement('INPUT'); + <% $field %>_input.setAttribute('name', '<% $field %>'+<% $prefix %>rownum); + <% $field %>_input.setAttribute('id', '<% $field %>'+<% $prefix %>rownum); + <% $field %>_input.setAttribute('type', 'text'); + <% $field %>_input.setAttribute('size', <% $size[$col] %>); + <% $field %>_input.setAttribute('maxlength', <% $maxl[$col] %>); + <% $field %>_input.style.textAlign = '<% $align[$col] %>'; + <% $field %>_input.onchange = possiblyAddRow; + <% $field %>_cell.appendChild(<% $field %>_input); + row.appendChild(<% $field %>_cell); +% } + var delcell = document.createElement('TD'); + var delinput = document.createElement('IMG'); + delinput.setAttribute('src', '<% "${p}images/cross.png" %>'); + delinput.setAttribute('alt', 'X'); + delinput.setAttribute('onclick', 'deleteThisRow(this);'); + delcell.appendChild(delinput); + row.appendChild(delcell); + + <% $prefix %>rownum++; + } + + function deleteThisRow(obj) { + if(<% $prefix %>rownum == rownum_of(obj)) { + <% $prefix %>addRow(); + } + <% $prefix %>table.deleteRow(rownum_of(obj)); + <% $prefix %>rownum--; + return(false); + } + + <% $prefix %>addRow(); +</SCRIPT> + +<%init> +my %opt = @_; + +my @header = @{ $opt{'header'} }; +my @fields = @{ $opt{'fields'} }; +my @data = (); +if($opt{'data'}) { + @data = @{ $opt{'data'} }; +} +elsif($opt{'records'}) { + foreach my $rec (@{ $opt{'records'} }) { + push @data, [ map { $rec->getfield($_) } @fields ]; + } +} +# else @data = (); + +my $prefix = $opt{'prefix'}; +my @size = $opt{'size'} ? @{ $opt{'size'} } : (map {16} @fields); +my @maxl = $opt{'maxl'} ? @{ $opt{'maxl'} } : @size; +my @align = $opt{'align'} ? @{ $opt{'align'} } : (map {'right'} @fields); + +</%init> diff --git a/httemplate/misc/delete-rate_detail.html b/httemplate/misc/delete-rate_detail.html new file mode 100755 index 000000000..30856a73a --- /dev/null +++ b/httemplate/misc/delete-rate_detail.html @@ -0,0 +1,20 @@ +% if ( $error ) { +% errorpage($error); +% } else { +<% header('Rate deleted') %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my ($query) = $cgi->keywords; +$query =~ /^(\d+)$/ or die "Illegal ratedetailnum"; +my $rate_detail = FS::rate_detail->by_key($1); +my $error = $rate_detail->delete; + +</%init> |