summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
authormark <mark>2010-07-01 01:53:50 +0000
committermark <mark>2010-07-01 01:53:50 +0000
commitde3bf373e97f6875e09fc17d99068a2716bf3b2d (patch)
treebf5d4908cb57519a99cb8db0266f40d671f9e170 /httemplate
parent6643a544400f5bed64b75720fdaccb41f01d1e79 (diff)
voip_cdr call rating by day and time, RT#4763
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/browse/rate.cgi2
-rw-r--r--httemplate/browse/rate_detail.html205
-rw-r--r--httemplate/browse/rate_time.html48
-rw-r--r--httemplate/edit/process/rate_time.cgi94
-rw-r--r--httemplate/edit/rate_detail.html9
-rw-r--r--httemplate/edit/rate_time.cgi44
-rw-r--r--httemplate/elements/auto-table.html146
-rwxr-xr-xhttemplate/misc/delete-rate_detail.html20
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 = '&nbsp;<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>