From 342e2bfbe6aad470ab9dee40640f8ac7d9653003 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 22 Jul 2010 00:11:56 +0000 Subject: [PATCH] cdr rating by day and time, part 2, RT#4763 --- FS/FS/rate_time_interval.pm | 24 ++++- httemplate/browse/rate_detail.html | 163 ++------------------------------ httemplate/edit/process/rate_region.cgi | 21 ++-- httemplate/edit/process/rate_time.cgi | 42 ++++---- httemplate/edit/rate.cgi | 8 +- httemplate/edit/rate_region.cgi | 114 ++-------------------- httemplate/edit/rate_time.cgi | 49 +++++++--- httemplate/elements/auto-table.html | 77 +++++++++------ httemplate/elements/menu.html | 1 + 9 files changed, 154 insertions(+), 345 deletions(-) diff --git a/FS/FS/rate_time_interval.pm b/FS/FS/rate_time_interval.pm index 37bc83bdf..1a82edbe5 100644 --- a/FS/FS/rate_time_interval.pm +++ b/FS/FS/rate_time_interval.pm @@ -3,6 +3,7 @@ package FS::rate_time_interval; use strict; use base qw( FS::Record ); use FS::Record qw( qsearch qsearchs ); +use List::Util 'first'; =head1 NAME @@ -96,7 +97,7 @@ returns the error, otherwise returns false. =item check -Checks all fields to make sure this is a valid example. If there is +Checks all fields to make sure this is a valid interval. If there is an error, returns the error, otherwise returns false. Called by the insert and replace methods. @@ -112,6 +113,21 @@ sub check { || $self->ut_number('ratetimenum') ; return $error if $error; + # Disallow backward intervals. As a special case, an etime of 0 + # should roll to the last second of the week. + $self->etime(7*24*60*60) if $self->etime == 0; + return "end of interval is before start" if ($self->etime < $self->stime); + + # Detect overlap between intervals within the same rate_time. + # Since intervals are added one at a time, we only need to look + # for an existing interval that contains one of the endpoints of + # this one or that is completely inside this one. + my $overlap = $self->rate_time->contains($self->stime + 1) || + $self->rate_time->contains($self->etime - 1) || + first { $self->stime <= $_->stime && $self->etime >= $_->etime } + ( $self->rate_time->intervals ); + return "interval overlap: (".join('-',$self->description).') with ('. + join('-',$overlap->description).')' if $overlap; $self->SUPER::check; } @@ -130,7 +146,8 @@ sub rate_time { =item description Returns two strings containing stime and etime, formatted -"Day HH:MM:SS AM/PM". Example: "Mon 5:00 AM". +"Day HH:MM AM/PM". Example: "Mon 5:00 AM". Seconds are +not displayed, so be careful. =cut @@ -139,11 +156,10 @@ my @days = qw(Sun Mon Tue Wed Thu Fri Sat); sub description { my $self = shift; return map { - sprintf('%s %02d:%02d:%02d %s', + sprintf('%s %02d:%02d %s', $days[int($_/86400) % 7], int($_/3600) % 12, int($_/60) % 60, - $_ % 60, (($_/3600) % 24 < 12) ? 'AM' : 'PM' ) } ( $self->stime, $self->etime ); } diff --git a/httemplate/browse/rate_detail.html b/httemplate/browse/rate_detail.html index faaec2c38..aef550513 100644 --- a/httemplate/browse/rate_detail.html +++ b/httemplate/browse/rate_detail.html @@ -1,58 +1,14 @@ <% include('/elements/init_overlib.html') %> <% include('/elements/header.html',$title) %> <% include('/elements/menubar.html',@menubar) %> -

-<% include('/elements/table-grid.html') %> - -% my $col = 0; -% foreach (@header) { -% my $hlink = $hlinks[$col]; - - <% $hlink ? qq!$_! : $_ %> - -% $col++; -% } #foreach @header - -% my $row = 0; -% foreach my $region (@rate_region) { -% $col = 0; -% foreach ($region->regionname, $region->prefixes_short) { - - <% $_ %> - -% } -% foreach my $rate_time (@rate_time, '') { - -% my $detail = $details[$row][$col]; -% if($detail) { - - - <% granularity_detail($detail) %> - <% min_included_detail($detail) %> - <% conn_charge_detail($detail) %> - -
<% edit_link($detail) %><% $money_char.$detail->min_charge %> - <% $detail->sec_granularity ? ' / minute':' / call' %> - <% $edit_hint %> -
<% $rate_time ? delete_link($detail) : '' %>
-% } -% else { #!$detail - <% add_link($ratenum, $region, $rate_time) %> -% } -% $col++; - -% } # foreach @rate_time - -% $row++; -% }# foreach @rate_region +<% include('/edit/elements/rate_detail.html', + 'ratenum' => $ratenum, + 'countrycode' => $countrycode, +) %> <% include('/elements/footer.html') %> <%once> -tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); -tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs(); - my $conf = new FS::Conf; my $money_char = $conf->config('money_char') || '$'; @@ -61,88 +17,6 @@ my @menubar = ( 'Rate plans' => $p.'browse/rate.cgi', 'Time Periods' => $p.'browse/rate_time.html', ); -sub small { - ''.shift.'' -} -my $edit_hint = small('(edit)'); - -sub edit_link { - my $rate_detail = shift; - my $ratedetailnum = $rate_detail->ratedetailnum; - ' 'Edit rate', - 'height' => 420, - #default# 'width' => 540, - #default# 'color' => '#333399', - ) . '">' -} - -sub add_link { - my ($ratenum, $region, $rate_time) = @_; - ' 'Add rate', - 'height' => 420, - ).'">'.small('(add)').'' -} - -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!!.small('(delete)').'' -} - -sub granularity_detail { - my $rate_detail = shift; - if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) { - ''. - small('in '.$granularity{$rate_detail->sec_granularity}.' increments'). - ''; - } - else { '' } -} - -sub min_included_detail { - my $rate_detail = shift; - if($rate_detail->min_included) { - ''. - small( $rate_detail->min_included . - ($rate_detail->sec_granularity ? - ' minutes included' : - ' calls included') ). - '' - } - 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; - ''. - small( $money_char. $rate_detail->conn_charge. - ' for '.$conn_secs{$rate_detail->conn_sec} - ). - '' - } - else { '' } -} - <%init> @@ -157,35 +31,10 @@ my $ratename = $rate->ratename; my $title = "$ratename rates"; my $where; +my $countrycode = ''; if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) { - my $countrycode = $1; - $where = "WHERE 0 < ( SELECT COUNT(*) FROM rate_prefix - WHERE rate_prefix.regionnum = rate_region.regionnum - AND countrycode = '$countrycode' - ) - "; + $countrycode = $1; $title .= " for +$countrycode"; } -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 ]; -} - diff --git a/httemplate/edit/process/rate_region.cgi b/httemplate/edit/process/rate_region.cgi index 8036f7388..d342e605a 100755 --- a/httemplate/edit/process/rate_region.cgi +++ b/httemplate/edit/process/rate_region.cgi @@ -1,6 +1,8 @@ %if ( $error ) { % $cgi->param('error', $error); <% $cgi->redirect(popurl(2). "rate_region.cgi?". $cgi->query_string ) %> +%} elsif ( $action eq 'Add' ) { +<% $cgi->redirect(popurl(2). "rate_region.cgi?$regionnum") %> %} else { <% $cgi->redirect(popurl(3). "browse/rate_region.html") %> %} @@ -11,6 +13,7 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); my $regionnum = $cgi->param('regionnum'); +my $action = $regionnum ? 'Edit' : 'Add'; my $old = qsearchs('rate_region', { 'regionnum' => $regionnum } ) if $regionnum; @@ -33,24 +36,12 @@ my @rate_prefix = map { 'npa' => $_, } } @npa; - -my @dest_detail = map { - my $ratenum = $_->ratenum; - new FS::rate_detail { - 'ratenum' => $ratenum, - map { $_ => $cgi->param("$_$ratenum") } - qw( min_included conn_charge conn_sec min_charge sec_granularity classnum ) - }; -} qsearch('rate', {} ); - - +# we no longer process dest_detail records here my $error; if ( $regionnum ) { - $error = $new->replace($old, 'rate_prefix' => \@rate_prefix, - 'dest_detail' => \@dest_detail, ); + $error = $new->replace($old, 'rate_prefix' => \@rate_prefix ); } else { - $error = $new->insert( 'rate_prefix' => \@rate_prefix, - 'dest_detail' => \@dest_detail, ); + $error = $new->insert( 'rate_prefix' => \@rate_prefix ); $regionnum = $new->getfield('regionnum'); } diff --git a/httemplate/edit/process/rate_time.cgi b/httemplate/edit/process/rate_time.cgi index 48ed2739e..4fa78ce6d 100644 --- a/httemplate/edit/process/rate_time.cgi +++ b/httemplate/edit/process/rate_time.cgi @@ -44,12 +44,13 @@ else { #!$ratetimenum, adding new $ratetimenum = $rate_time->ratetimenum; } +my @new_ints; 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"; + foreach my $i (map { /^sd(\d+)$/ } keys(%vars)) { + my $stime = l2wtime(@vars{"sd$i", "sh$i", "sm$i", "sa$i"}); + my $etime = l2wtime(@vars{"ed$i", "eh$i", "em$i", "ea$i"}); + #warn "$i: $stime - $etime"; + next if !defined($stime) or !defined($etime) or $etime == $stime; # try to avoid needlessly wiping and replacing intervals every # time this is edited. if( %old_ints ) { @@ -63,12 +64,9 @@ if(!$delete and !$error) { 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; + push @new_ints, FS::rate_time_interval->new({ ratetimenum => $ratetimenum, + stime => $stime, + etime => $etime, } ); } } if(!$error) { @@ -78,17 +76,19 @@ if(!$error) { last if $error; } } +if(!$error) { + # do this last to avoid overlap errors with deleted intervals + foreach (@new_ints) { + $error = $_->insert; + #warn "inserting $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); +sub l2wtime { + my ($d, $h, $m, $a) = @_; + $h += 24*$d + 12*$a; $m += 60*$h; - $s += 60*$m; - return $s; + return 60*$m } diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi index 75c714322..13717dc1a 100644 --- a/httemplate/edit/rate.cgi +++ b/httemplate/edit/rate.cgi @@ -22,9 +22,15 @@ Rate plan " onClick="document.OneTrueForm.submit.disabled=true; process();"> - +% if($rate->ratenum) { +

Rates in this plan +<% include('/edit/elements/rate_detail.html', + 'ratenum' => $rate->ratenum +) %> +% } + <% include('/elements/footer.html') %> <%init> diff --git a/httemplate/edit/rate_region.cgi b/httemplate/edit/rate_region.cgi index f77c0dbe7..cae30030a 100644 --- a/httemplate/edit/rate_region.cgi +++ b/httemplate/edit/rate_region.cgi @@ -35,113 +35,19 @@ -%# rate plan info - -
- -<% include('/elements/table-grid.html') %> -% my $bgcolor1 = '#eeeeee'; -% my $bgcolor2 = '#ffffff'; -% my $bgcolor = ''; - - - - Rate plan - - - Included
minutes/calls
- - - Connection
charge
- - - Connection
charge for
- - - Charge per
minute/call
- - - Granularity - - - Usage class - - - -% foreach my $rate ( qsearch('rate', {}) ) { -% -% my $n = $rate->ratenum; -% my $rate_detail = $rate->dest_detail($rate_region) -% || new FS::rate_region { 'min_included' => 0, -% 'min_charge' => 0, -% 'sec_granularity' => '60' -% }; -% -% if ( $bgcolor eq $bgcolor1 ) { -% $bgcolor = $bgcolor2; -% } else { -% $bgcolor = $bgcolor1; -% } - - - - - <% $rate->ratename %> - - - - min_included |h %>"> - - - - <%$money_char%>conn_charge |h %>"> - - - - min_charge |h %>"> - - - - - - - - <% include( '/elements/select-table.html', - 'element_name' => "classnum$n", - 'table' => 'usage_class', - 'name_col' => 'classname', - 'empty_label' => '(default)', - 'hashref' => { disabled => '' }, - 'curr_value' => ( $cgi->param("classnum$n") || - $rate_detail->classnum ), - ) - %> - - - - -% } - - - -

"> - +%# rate plan info, if the region has been created yet + +% if($rate_region->regionnum) { +
+
+Rates in this region +<% include('/edit/elements/rate_detail.html', + 'regionnum' => $rate_region->regionnum, +) %> +% } <% include('/elements/footer.html') %> <%once> diff --git a/httemplate/edit/rate_time.cgi b/httemplate/edit/rate_time.cgi index 230aef82d..eca8fbb61 100644 --- a/httemplate/edit/rate_time.cgi +++ b/httemplate/edit/rate_time.cgi @@ -1,9 +1,10 @@ -<% include("/elements/header.html","$action Time Period", menubar( +<% include("/elements/header.html", { title => "$action Time Period" }) %> +<% include("/elements/menubar.html", 'Rate plans' => "${p}browse/rate.cgi", - ) ) -%> - + ) %> +
<% include('/elements/error.html') %> +
"> @@ -15,12 +16,10 @@ <% include('/elements/auto-table.html', - header => [ 'Start', 'End' ], - fields => [ 'stime', 'etime' ], - size => [ 18, 18 ], - maxl => [ 15, 15 ], - align => [ 'right', 'right' ], - data => \@data, + 'header' => [ '', 'Start','','', '','End','','' ], + 'fields' => [ qw(sd sh sm sa ed eh em ea) ], + 'select' => [ ($day, $hour, $min, $ampm) x 2 ], + 'data' => \@data, ) %>
@@ -29,16 +28,42 @@ <% include('/elements/footer.html') %> <%init> -my $ratetimenum = ($cgi->keywords)[0] || ''; +my $ratetimenum = ($cgi->keywords)[0] || $cgi->param('ratetimenum') || ''; my $action = 'Add'; my $rate_time; my @data = (); +my $day = [ 0 => 'Sun', + 1 => 'Mon', + 2 => 'Tue', + 3 => 'Wed', + 4 => 'Thu', + 5 => 'Fri', + 6 => 'Sat', ]; +my $hour = [ map( {$_, sprintf('%02d',$_) } 0..11 )]; +my $min = [ map( {$_, sprintf('%02d',$_) } 0,30 )]; +my $ampm = [ 0 => 'AM', 1 => 'PM' ]; if($ratetimenum) { $action = 'Edit'; $rate_time = qsearchs('rate_time', {ratetimenum => $ratetimenum}) or die "ratetimenum $ratetimenum not found"; - @data = $rate_time->description; + if($cgi->param('error')) { + my %vars = $cgi->Vars; + foreach my $i (sort {$a <=> $b } map { /^sd(\d+)$/ } keys(%vars)) { + push @data, [ @vars{"sd$i", "sh$i", "sm$i", "sa$i", + "ed$i", "eh$i", "em$i", "ea$i"} ]; + } + } + else { + foreach my $interval ($rate_time->intervals) { + push @data, [ map { int($_/86400) % 7, + int($_/3600) % 12, + int($_/60) % 60, + int($_/43200) % 2, } + ( $interval->stime, $interval->etime ) + ]; + } + } } diff --git a/httemplate/elements/auto-table.html b/httemplate/elements/auto-table.html index 89d6eacb9..9c7dfd09a 100644 --- a/httemplate/elements/auto-table.html +++ b/httemplate/elements/auto-table.html @@ -28,6 +28,12 @@ Example: 'records' => [ qsearch('item', { } ) ], # or any other array of FS::Record objects + 'select' => [ '', + [ 1 => 'option 1', + 2 => 'option 2', ... + ], # options for second field + '' ], + 'prefix' => 'mytable_', ) %> @@ -47,6 +53,17 @@ Values will be passed through as "mytable_id1", etc. % for ( $col = 0; $col < scalar @fields; $col++ ) { % my $id = $prefix . $fields[$col] . $row; +% my @o = @{ $select[$col] }; +% if( @o ) { + +% } +% else { STYLE = "text-align:<% $align[$col] %>" VALUE = "<% $data[$row][$col] %>" - onchange = "possiblyAddRow();" +% if( $opt{'autoadd'} ) { + onchange = "possiblyAddRow(this);" +% } > +% } % } " ALT = "X" - onclick = "deleteThisRow(this);" + onclick = "deleteRow(this);" > % } +% if( !$opt{'autoadd'} ) { +
+% } <%init> @@ -137,10 +148,14 @@ elsif($opt{'records'}) { } } # else @data = (); +push @data, [ map {''} @fields ]; # make a blank row 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); - +my @select = @{ $opt{'select'} || [] }; +foreach (0..scalar(@fields)-1) { + $select[$_] ||= []; +} diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 1eaefe231..da2dcaa4c 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -421,6 +421,7 @@ tie my %config_billing_rates, 'Tie::IxHash', 'Rate plans' => [ $fsurl.'browse/rate.cgi', 'Manage rate plans' ], 'Regions and prefixes' => [ $fsurl.'browse/rate_region.html', 'Manage regions and prefixes' ], 'Usage classes' => [ $fsurl.'browse/usage_class.html', 'Usage classes define groups of usage for taxation.' ], + 'Time periods' => [ $fsurl.'browse/rate_time.html', 'Time periods define days and hours for rate plans' ], 'Edit rates with Excel' => [ $fsurl.'misc/rate_edit_excel.html', 'Download and edit rates with Excel, then upload changes.' ], #"Edit with Excel" ? ; -- 2.11.0