summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Schema.pm11
-rw-r--r--FS/FS/cdr.pm20
-rw-r--r--FS/FS/part_pkg/agent_cdr.pm8
-rw-r--r--FS/FS/rate.pm31
-rw-r--r--FS/FS/rate_detail.pm20
-rw-r--r--httemplate/edit/elements/rate_detail.html102
-rw-r--r--httemplate/edit/process/rate_detail.html32
-rw-r--r--httemplate/edit/rate_detail.html22
8 files changed, 191 insertions, 55 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index ba39d80..0b82f91 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4808,9 +4808,10 @@ sub tables_hashref {
'rate' => {
'columns' => [
- 'ratenum', 'serial', '', '', '', '',
- 'ratename', 'varchar', '', $char_d, '', '',
- 'agentnum', 'int', 'NULL', '', '', '',
+ 'ratenum', 'serial', '', '', '', '',
+ 'ratename', 'varchar', '',$char_d, '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ 'default_detailnum', 'int', 'NULL', '', '', '',
],
'primary_key' => 'ratenum',
'unique' => [],
@@ -4827,7 +4828,7 @@ sub tables_hashref {
'ratedetailnum', 'serial', '', '', '', '',
'ratenum', 'int', '', '', '', '',
'orig_regionnum', 'int', 'NULL', '', '', '',
- 'dest_regionnum', 'int', '', '', '', '',
+ 'dest_regionnum', 'int', 'NULL', '', '', '',
'min_included', 'int', '', '', '', '',
'conn_charge', 'decimal', '', '10,4', '0.0000', '',
'conn_cost', 'decimal', '', '10,4', '0.0000', '',
@@ -4839,6 +4840,8 @@ sub tables_hashref {
'classnum', 'int', 'NULL', '', '', '',
'cdrtypenum', 'int', 'NULL', '', '', '',
'region_group', 'char', 'NULL', 1, '', '',
+ 'upstream_mult_charge', 'decimal', '', '10,4', '0.0000', '',
+ 'upstream_mult_cost', 'decimal', '', '10,4', '0.0000', '',
],
'primary_key' => 'ratedetailnum',
'unique' => [ [ 'ratenum', 'orig_regionnum', 'dest_regionnum' ] ],
diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm
index 9859dfa..7a5668d 100644
--- a/FS/FS/cdr.pm
+++ b/FS/FS/cdr.pm
@@ -799,8 +799,8 @@ sub rate_prefix {
}
+ my $regionnum = $rate_detail->dest_regionnum;
my $rate_region = $rate_detail->dest_region;
- my $regionnum = $rate_region->regionnum;
warn " found rate for regionnum $regionnum ".
"and rate detail $rate_detail\n"
if $DEBUG;
@@ -842,6 +842,11 @@ sub rate_prefix {
my $charge = 0;
my $connection_charged = 0;
+ # before doing anything else, if there's an upstream multiplier and
+ # an upstream price, add that to the charge. (usually the rate detail
+ # will then have a minute charge of zero, but not necessarily.)
+ $charge += ($self->upstream_price || 0) * $rate_detail->upstream_mult_charge;
+
my $etime;
while($seconds_left) {
my $ratetimenum = $rate_detail->ratetimenum; # may be empty
@@ -989,7 +994,7 @@ sub rate_prefix {
$price,
$opt{'svcnum'},
'rated_pretty_dst' => $pretty_dst,
- 'rated_regionname' => $rate_region->regionname,
+ 'rated_regionname' => ($rate_region ? $rate_region->regionname : ''),
'rated_seconds' => $rated_seconds, #$seconds,
'rated_granularity' => $rate_detail->sec_granularity, #$granularity
'rated_ratedetailnum' => $rate_detail->ratedetailnum,
@@ -1073,10 +1078,15 @@ sub rate_cost {
my $rate_detail =
qsearchs('rate_detail', { 'ratedetailnum' => $self->rated_ratedetailnum } );
- return $rate_detail->min_cost if $self->rated_granularity == 0;
+ my $charge = 0;
+ $charge += ($self->upstream_price || 0) * ($rate_detail->upstream_mult_cost);
- my $minutes = $self->rated_seconds / 60;
- my $charge = $rate_detail->conn_cost + $minutes * $rate_detail->min_cost;
+ if ( $self->rated_granularity == 0 ) {
+ $charge += $rate_detail->min_cost;
+ } else {
+ my $minutes = $self->rated_seconds / 60;
+ $charge += $rate_detail->conn_cost + $minutes * $rate_detail->min_cost;
+ }
sprintf('%.2f', $charge + .00001 );
diff --git a/FS/FS/part_pkg/agent_cdr.pm b/FS/FS/part_pkg/agent_cdr.pm
index 55792f2..a638b5b 100644
--- a/FS/FS/part_pkg/agent_cdr.pm
+++ b/FS/FS/part_pkg/agent_cdr.pm
@@ -23,7 +23,7 @@ tie my %temporalities, 'Tie::IxHash',
%info = (
'name' => 'Wholesale CDR cost billing, for master customers of an agent.',
- 'shortname' => 'Whilesale CDR cost billing for agent.',
+ 'shortname' => 'Wholesale CDR cost billing for agent',
'inherit_fields' => [ 'prorate_Mixin', 'global_Mixin' ],
'fields' => { #false laziness w/cdr_termination
@@ -177,7 +177,11 @@ sub calc_recur {
my $classnum = ''; #usage class?
#option to turn off? or just use squelch_cdr for the customer probably
- push @$details, [ 'C', $call_details, $cost, $classnum ];
+ # XXX use detail_format for this at some point
+ push @$details, { 'format' => 'C',
+ 'detail' => $call_details,
+ 'amount' => $cost,
+ 'classnum' => $classnum };
#eofalse laziness w/cdr_termination
diff --git a/FS/FS/rate.pm b/FS/FS/rate.pm
index aef9d8b..9a5b905 100644
--- a/FS/FS/rate.pm
+++ b/FS/FS/rate.pm
@@ -46,6 +46,16 @@ Rate name
Optional agent (see L<FS::agent>) for agent-virtualized rates.
+=item default_detailnum
+
+Optional rate detail to apply when a call doesn't match any region in the
+rate plan. If this is not set, the call will either be left unrated (though
+it may still be processed under a different pricing addon package), or be
+marked as 'skipped', or throw a fatal error, depending on the setting of
+the 'ignore_unrateable' package option.
+
+=item
+
=back
=head1 METHODS
@@ -268,6 +278,7 @@ sub check {
$self->ut_numbern('ratenum')
|| $self->ut_text('ratename')
#|| $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+ || $self->ut_numbern('default_detailnum')
;
return $error if $error;
@@ -277,8 +288,8 @@ sub check {
=item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
Returns the rate detail (see L<FS::rate_detail>) for this rate to the
-specificed destination, or the empty string if no rate can be found for
-the given destination.
+specificed destination. If no rate can be found, returns the default
+rate if there is one, and an empty string otherwise.
Destination can be specified as an FS::rate_detail object or regionnum
(see L<FS::rate_detail>), or as a hashref containing the following keys:
@@ -379,8 +390,8 @@ sub dest_detail {
foreach (@details) {
return $_ if $_->ratetimenum eq '';
}
- # found nothing
- return;
+ # if still nothing, return the global default rate for this plan
+ return $self->default_detail;
}
=item rate_detail
@@ -389,6 +400,18 @@ Returns all region-specific details (see L<FS::rate_detail>) for this rate.
=back
+=item default_detail
+
+Returns the default rate detail, if there is one.
+
+=cut
+
+sub default_detail {
+ my $self = shift;
+ $self->default_detailnum ?
+ FS::rate_detail->by_key($self->default_detailnum) : ''
+}
+
=head1 SUBROUTINES
=over 4
diff --git a/FS/FS/rate_detail.pm b/FS/FS/rate_detail.pm
index d81d9db..d50c89f 100644
--- a/FS/FS/rate_detail.pm
+++ b/FS/FS/rate_detail.pm
@@ -60,6 +60,13 @@ inherits from FS::Record. The following fields are currently supported:
=item region_group - Group in region group for rate plan
+=item upstream_mult_charge - the multiplier to apply to the upstream price.
+Defaults to zero, and should stay zero unless this rate is intended to include
+a markup on pre-rated CDRs.
+
+=item upstream_mult_cost - the multiplier to apply to the upstream price to
+calculate the wholesale cost.
+
=back
=head1 METHODS
@@ -124,7 +131,7 @@ sub check {
$self->ut_numbern('ratedetailnum')
|| $self->ut_foreign_key('ratenum', 'rate', 'ratenum')
|| $self->ut_foreign_keyn('orig_regionnum', 'rate_region', 'regionnum' )
- || $self->ut_foreign_key('dest_regionnum', 'rate_region', 'regionnum' )
+ || $self->ut_foreign_keyn('dest_regionnum', 'rate_region', 'regionnum' )
|| $self->ut_number('min_included')
#|| $self->ut_money('min_charge')
@@ -138,6 +145,9 @@ sub check {
|| $self->ut_foreign_keyn('classnum', 'usage_class', 'classnum' )
|| $self->ut_enum('region_group', [ '', 'Y' ])
+
+ || $self->ut_floatn('upstream_mult_charge')
+ || $self->ut_floatn('upstream_mult_cost')
;
return $error if $error;
@@ -182,10 +192,11 @@ with this call plan rate.
sub dest_regionname {
my $self = shift;
- $self->dest_region->regionname;
+ my $dest_region = $self->dest_region;
+ $dest_region ? $dest_region->regionname : 'Global default';
}
-=item dest_regionname
+=item dest_prefixes_short
Returns a short list of the prefixes for the destination region
(see L<FS::rate_region>) associated with this call plan rate.
@@ -194,7 +205,8 @@ Returns a short list of the prefixes for the destination region
sub dest_prefixes_short {
my $self = shift;
- $self->dest_region->prefixes_short;
+ my $dest_region = $self->dest_region;
+ $dest_region ? $dest_region->prefixes_short : '';
}
=item rate_time
diff --git a/httemplate/edit/elements/rate_detail.html b/httemplate/edit/elements/rate_detail.html
index 14b5211..7b5ec31 100644
--- a/httemplate/edit/elements/rate_detail.html
+++ b/httemplate/edit/elements/rate_detail.html
@@ -47,34 +47,85 @@ with row headers showing the region name and prefixes.
% }
% foreach my $rate_time (@rate_time, '') {
<TD>
-% my $detail = $details[$row][$col];
-% if($detail) {
+ <& .detail_box,
+ detail => $details[$row][$col],
+ ratetimenum => ($rate_time ? $rate_time->ratetimenum : ''),
+ cdrtypenum => $cdrtypenum,
+ regionnum => $region->regionnum,
+ ratenum => $rate->ratenum
+ &>
+% $col++;
+ </TD>
+% } # foreach @rate_time
+</TR>
+% $row++;
+% }# foreach @rate_region
+% if ( !$opt{regionnum} ) {
+%# global default
+<TR>
+ <TD COLSPAN=2 STYLE="padding-top: 10px">
+ <B>Global default</B> (for calls not matching any prefix)
+ </TD>
+ <TD STYLE="padding-top: 10px">
+% # default rate: set a null region
+ <B>
+ <& .detail_box,
+ detail => $rate->default_detail,
+ ratetimenum => '',
+ cdrtypenum => '',
+ regionnum => '',
+ ratenum => $rate->ratenum
+ &>
+ </B>
+ </TD>
+% }
+</TABLE>
+<%def .detail_box>
+<%args>
+$detail => undef,
+$ratetimenum
+$cdrtypenum
+$regionnum
+$ratenum
+</%args>
+% if ($detail) {
<TABLE CLASS="inv" STYLE="border:none">
- <TR><TD><% edit_link($detail) %><% $money_char.$detail->min_charge %>
+ <TR><TD><% edit_link($detail) %>
+% if ( $detail->min_charge > 0 or $detail->conn_charge > 0) {
+ <% $money_char.$detail->min_charge %>
<% $detail->sec_granularity ? ' / minute':' / call' %>
% if ( $detail->min_cost ) {
(<% $money_char.$detail->min_cost %> cost)
% }
+% if ( $detail->upstream_mult_charge > 0
+% or $detail->upstream_mult_cost > 0) {
+ <BR>+
+% }
+% }
+% if ( $detail->upstream_mult_charge > 0
+% or $detail->upstream_mult_cost > 0) {
+ <% $detail->upstream_mult_charge %> &times; upstream price
+% if ( $detail->upstream_mult_cost > 0 ) {
+ (<% $detail->upstream_mult_cost %> cost)
+% }
+% }
+% if ( $detail->upstream_mult_charge == 0
+% and $detail->min_charge == 0
+% and $detail->conn_charge == 0 ) {
+ Free
+% }
<% $edit_hint %></A>
</TD></TR>
<% granularity_detail($detail) %>
<% min_included_detail($detail) %>
<% conn_charge_detail($detail) %>
- <TR><TD><% ( $rate_time || $cdrtypenum ) ? delete_link($detail) : '' %>
+ <TR><TD><% ( $ratetimenum || $cdrtypenum ) ? delete_link($detail) : '' %>
</TD></TR>
</TABLE>
-% }
-% else { #!$detail
- <% add_link($rate, $region, $rate_time, $cdrtypenum) %>
-% }
-% $col++;
- </TD>
-% } # foreach @rate_time
-</TR>
-% $row++;
-% }# foreach @rate_region
-</TABLE>
-
+% } else {
+ <% add_link($ratenum, $regionnum, $ratetimenum, $cdrtypenum) %>
+% }
+</%def>
<%once>
tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
@@ -95,25 +146,27 @@ sub edit_link {
include( '/elements/popup_link_onclick.html',
'action' => "${p}edit/rate_detail.html?$ratedetailnum",
'actionlabel' => 'Edit rate',
- 'height' => 460,
+ 'height' => 550,
+ 'width' => 580,
#default# 'width' => 540,
#default# 'color' => '#333399',
) . '">'
}
sub add_link {
- my ($rate, $region, $rate_time, $cdrtypenum) = @_;
+ my ($ratenum, $regionnum, $ratetimenum, $cdrtypenum) = @_;
'<A HREF="javascript:void(0);" onclick="'.
include( '/elements/popup_link_onclick.html',
'action' => "${p}edit/rate_detail.html?ratenum=".
- $rate->ratenum.
+ $ratenum.
';dest_regionnum='.
- $region->regionnum.
+ $regionnum.
';ratetimenum='.
- ($rate_time ? $rate_time->ratetimenum : '').
+ ($ratetimenum || '').
";cdrtypenum=$cdrtypenum",
'actionlabel' => 'Add rate',
- 'height' => 460,
+ 'width' => 580,
+ 'height' => 550,
).'">'.small('(add)').'</A>'
}
@@ -133,7 +186,10 @@ sub delete_link {
sub granularity_detail {
my $rate_detail = shift;
- if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) {
+ if(
+ $rate_detail->sec_granularity != 60
+ && $rate_detail->sec_granularity > 0
+ && $rate_detail->min_charge > 0) {
'<TR><TD>'.
small('in '.$granularity{$rate_detail->sec_granularity}.' increments').
'</TD></TR>';
diff --git a/httemplate/edit/process/rate_detail.html b/httemplate/edit/process/rate_detail.html
index 6200d61..0709d50 100644
--- a/httemplate/edit/process/rate_detail.html
+++ b/httemplate/edit/process/rate_detail.html
@@ -1,13 +1,35 @@
-<% include( 'elements/process.html',
- 'table' => 'rate_detail',
- 'popup_reload' => 'Rate changed', #a popup "parent reload" for now
+<& elements/process.html,
+ 'table' => 'rate_detail',
+ 'popup_reload' => 'Rate changed', #a popup "parent reload" for now
#someday change the individual element and go away instead
- )
-%>
+ 'noerror_callback' => $set_default_detail
+&>
<%init>
my $conf = new FS::Conf;
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+my $set_default_detail = sub {
+ my ($cgi, $rate_detail) = @_;
+warn Dumper $rate_detail;
+ if (!$rate_detail->dest_regionnum) {
+ # then this is a global default rate
+ my $rate = $rate_detail->rate;
+ if ($rate->default_detailnum) {
+ if ($rate->default_detailnum == $rate_detail->ratedetailnum) {
+ return;
+ } else {
+ # there's somehow an existing default rate. remove it.
+ my $old_default = $rate->default_detail;
+ my $error = $old_default->delete;
+ die "$error (removing old default rate)\n" if $error;
+ }
+ }
+ $rate->set('default_detailnum' => $rate_detail->ratedetailnum);
+ my $error = $rate->replace;
+ die "$error (setting default rate)\n" if $error;
+ }
+};
+
</%init>
diff --git a/httemplate/edit/rate_detail.html b/httemplate/edit/rate_detail.html
index 0de6ecc..3e80072 100644
--- a/httemplate/edit/rate_detail.html
+++ b/httemplate/edit/rate_detail.html
@@ -15,6 +15,8 @@
'conn_cost' => 'Wholesale connection cost',
'min_cost' => 'Wholesale cost per minute/call',
'classnum' => 'Usage class',
+ 'upstream_mult_charge'=> 'Upstream multiplier (retail)',
+ 'upstream_mult_cost' => 'Upstream multiplier (cost)',
},
'fields' => [
{ field=>'ratenum', type=>'hidden', },
@@ -46,13 +48,15 @@
labels => \%granularity,
disable_empty => 1,
},
- { field =>'classnum',
- type =>'select-table',
- table =>'usage_class',
- name_col =>'classname',
- empty_label =>'(default)',
- hashref =>{ disabled => '' },
+ { field => 'classnum',
+ type => 'select-table',
+ table => 'usage_class',
+ name_col => 'classname',
+ empty_label => '(default)',
+ hashref => { disabled => '' },
},
+ { field => 'upstream_mult_charge', type => 'text', },
+ { field => 'upstream_mult_cost', type => 'text', },
],
'new_hashref_callback' => sub {
@@ -62,6 +66,8 @@
cdrtypenum => scalar($cgi->param('cdrtypenum')),
min_included => 0,
conn_charge => 0,
+ upstream_mult_charge => 0,
+ upstream_mult_cost => 0,
}
},
)
@@ -85,8 +91,8 @@ if ( $keywords =~ /^(\d+)$/
|| $cgi->param('ratedetailnum') =~ /^(\d+)$/ ) {
my $rate_detail = qsearchs('rate_detail', { 'ratedetailnum' => $1 } )
or die "unknown ratedetailnum $1";
- $name =
- $rate_detail->rate->ratename. ' rate for '. $rate_detail->dest_regionname;
+ $name = $rate_detail->rate->ratename. ' rate for '.
+ ($rate_detail->dest_regionname || 'global default');
}
#sec_granularity should default to 60! for new rates when this gets used for em