summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Report/Table.pm42
-rw-r--r--FS/FS/Schema.pm4
-rw-r--r--FS/FS/cdr.pm20
-rw-r--r--FS/FS/cust_bill_pkg_detail.pm37
-rw-r--r--FS/FS/detail_format.pm2
-rw-r--r--FS/FS/detail_format/sum_count.pm4
-rw-r--r--FS/FS/detail_format/sum_duration.pm4
-rw-r--r--FS/FS/detail_format/sum_duration_prefix.pm4
8 files changed, 106 insertions, 11 deletions
diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm
index eeb99bac5..5fb56404d 100644
--- a/FS/FS/Report/Table.pm
+++ b/FS/FS/Report/Table.pm
@@ -599,6 +599,10 @@ sub _cust_bill_pkg_recurring {
$self->in_time_period_and_agent($speriod, $eperiod, $agentnum, $_date);
}
+ if ( $opt{'custnum'} =~ /^(\d+)$/ ) {
+ push @where, "(cust_main.custnum = $1)";
+ }
+
return "
FROM $cust_bill_pkg
$cust_bill_pkg_join
@@ -606,6 +610,16 @@ sub _cust_bill_pkg_recurring {
}
+=item cust_bill_pkg_recur: the total recur charges
+
+Most arguments as for C<cust_bill_pkg>, plus:
+
+'custnum': limit to this customer
+
+'cost': if true, return total recur costs instead
+
+=cut
+
sub cust_bill_pkg_recur {
my $self = shift;
my ($speriod, $eperiod, $agentnum, %opt) = @_;
@@ -632,9 +646,11 @@ sub cust_bill_pkg_recur {
($cust_bill_pkg.edate - $cust_bill_pkg.sdate)";
}
- my $total_sql =
- "SELECT COALESCE(SUM(($cust_bill_pkg.recur - $item_usage) $recur_fraction),0)" .
- $self->_cust_bill_pkg_recurring(@_);
+ my $total_sql = $opt{'cost'}
+ ? "SELECT SUM(part_pkg.recur_cost)"
+ : "SELECT COALESCE(SUM(($cust_bill_pkg.recur - $item_usage) $recur_fraction),0)";
+
+ $total_sql .= $self->_cust_bill_pkg_recurring(@_);
$self->scalar_sql($total_sql);
}
@@ -650,10 +666,14 @@ sub cust_bill_pkg_count_pkgnum {
=item cust_bill_pkg_detail: the total usage charges in detail lines.
-Arguments as for C<cust_bill_pkg>, plus:
+Most arguments as for C<cust_bill_pkg>, plus:
'usageclass': limit to this usage class number.
+'custnum': limit to this customer
+
+'cost': if true, return total usage costs instead
+
=cut
sub cust_bill_pkg_detail {
@@ -686,7 +706,16 @@ sub cust_bill_pkg_detail {
);
}
+ if ( $opt{'custnum'} =~ /^(\d+)$/ ) {
+ push @where, "(cust_main.custnum = $1)";
+ }
+
my $total_sql = " SELECT SUM(cust_bill_pkg_detail.amount) ";
+ my $extra_join = '';
+ if ($opt{'cost'}) {
+ $extra_join = " JOIN cdr USING ( detailnum ) ";
+ $total_sql = " SELECT SUM(cdr.rated_cost) ";
+ }
$total_sql .=
" FROM cust_bill_pkg_detail
@@ -696,8 +725,9 @@ sub cust_bill_pkg_detail {
LEFT JOIN cust_pkg ON cust_bill_pkg.pkgnum = cust_pkg.pkgnum
LEFT JOIN part_pkg USING ( pkgpart )
LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart
- LEFT JOIN part_fee USING ( feepart )
- WHERE ".join( ' AND ', grep $_, @where );
+ LEFT JOIN part_fee USING ( feepart )
+ ".$extra_join.
+ " WHERE ".join( ' AND ', grep $_, @where );
$self->scalar_sql($total_sql);
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index c855b1fba..a71b90249 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3819,6 +3819,7 @@ sub tables_hashref {
'rated_ratedetailnum', 'int', 'NULL', '', '', '',
'rated_classnum', 'int', 'NULL', '', '', '',
'rated_ratename', 'varchar', 'NULL', $char_d, '', '',
+ 'rated_cost', 'decimal', 'NULL', '10,4', '', '',
'carrierid', 'bigint', 'NULL', '', '', '',
@@ -3843,6 +3844,9 @@ sub tables_hashref {
#new
'cdrbatchnum', 'int', 'NULL', '', '', '',
+ # FK to cust_bill_pkg_detail; having a value here absolutely means
+ # that the CDR appears on an invoice
+ 'detailnum', 'bigint', 'NULL', '', '', '',
],
'primary_key' => 'acctid',
'unique' => [],
diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm
index cdca6fc75..8ccf7af63 100644
--- a/FS/FS/cdr.pm
+++ b/FS/FS/cdr.pm
@@ -161,6 +161,8 @@ following fields are currently supported:
=item cdrbatch
+=item detailnum - Link to invoice detail (L<FS::cust_bill_pkg_detail>)
+
=back
=head1 METHODS
@@ -214,6 +216,7 @@ sub table_info {
#'upstream_rateplanid' => '',
#'ratedetailnum' => '',
'rated_price' => 'Rated price',
+ 'rated_cost' => 'Rated cost',
#'distance' => '',
#'islocal' => '',
#'calltypenum' => '',
@@ -226,6 +229,7 @@ sub table_info {
'freesiderewritestatus' => 'Freeside rewrite status',
'cdrbatch' => 'Legacy batch',
'cdrbatchnum' => 'Batch',
+ 'detailnum' => 'Freeside invoice detail line',
},
};
@@ -337,8 +341,12 @@ sub check {
#check the foreign keys even?
#do we want to outright *reject* the CDR?
- my $error =
- $self->ut_numbern('acctid');
+ my $error = $self->ut_numbern('acctid');
+ return $error if $error;
+
+ if ( $self->freesidestatus ne 'done' ) {
+ $self->set('detailnum', ''); # can't have this on an unbilled call
+ }
#add a config option to turn these back on if someone needs 'em
#
@@ -351,8 +359,6 @@ sub check {
# # Telstra =1, Optus = 2, RSL COM = 3
# || $self->ut_foreign_keyn('carrierid', 'cdr_carrier', 'carrierid' )
- return $error if $error;
-
$self->SUPER::check;
}
@@ -463,7 +469,9 @@ Sets the status and rated price.
Available options are: inbound, rated_pretty_dst, rated_regionname,
rated_seconds, rated_minutes, rated_granularity, rated_ratedetailnum,
-rated_classnum, rated_ratename.
+rated_classnum, rated_ratename. If rated_ratedetailnum is provided,
+will also set a recalculated L</rate_cost> in the rated_cost field
+after the other fields are set (does not work with inbound.)
If there is an error, returns the error, otherwise returns false.
@@ -501,6 +509,8 @@ sub set_status_and_rated_price {
qw( pretty_dst regionname seconds minutes granularity
ratedetailnum classnum ratename );
$self->svcnum($svcnum) if $svcnum;
+ $self->rated_cost($self->rate_cost) if $opt{'rated_ratedetailnum'};
+
return $self->replace();
}
diff --git a/FS/FS/cust_bill_pkg_detail.pm b/FS/FS/cust_bill_pkg_detail.pm
index d0cbdbec0..dd118c1b2 100644
--- a/FS/FS/cust_bill_pkg_detail.pm
+++ b/FS/FS/cust_bill_pkg_detail.pm
@@ -86,15 +86,52 @@ sub table { 'cust_bill_pkg_detail'; }
Adds this record to the database. If there is an error, returns the error,
otherwise returns false.
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error = $self->SUPER::insert(@_);
+ return $error if $error;
+
+ # link CDRs
+ my $acctids = $self->get('acctid') or return '';
+ $acctids = [ $acctids ] unless ref $acctids;
+ foreach my $acctid ( @$acctids ) {
+ my $cdr = FS::cdr->by_key($acctid);
+ $cdr->set('detailnum', $self->detailnum);
+ $error = $cdr->replace;
+ # this should never happen
+ return "error linking CDR #$acctid: $error" if $error;
+ }
+ '';
+}
+
=item delete
Delete this record from the database.
+=cut
+
+sub delete {
+ my $self = shift;
+ my $error = $self->SUPER::delete;
+ return $error if $error;
+ foreach my $cdr (qsearch('cdr', { detailnum => $self->detailnum })) {
+ $cdr->set('detailnum', '');
+ $error = $cdr->replace;
+ return "error unlinking CDR #" . $cdr->acctid . ": $error" if $error;
+ }
+}
+
=item replace OLD_RECORD
Replaces the OLD_RECORD with this one in the database. If there is an error,
returns the error, otherwise returns false.
+=cut
+
+# the replace method can be inherited from FS::Record (doesn't touch CDRs)
+
=item check
Checks all fields to make sure this is a valid line item detail. If there is
diff --git a/FS/FS/detail_format.pm b/FS/FS/detail_format.pm
index 8840a006d..be84680f9 100644
--- a/FS/FS/detail_format.pm
+++ b/FS/FS/detail_format.pm
@@ -178,6 +178,7 @@ Takes a single CDR and returns an invoice detail to describe it.
By default, this maps the following fields from the CDR:
+acctid => acctid
rated_price => amount
rated_classnum => classnum
rated_seconds => duration
@@ -208,6 +209,7 @@ sub single_detail {
$price = 0 if $cdr->freesidestatus eq 'no-charge';
FS::cust_bill_pkg_detail->new( {
+ 'acctid' => $cdr->acctid,
'amount' => $price,
'classnum' => $cdr->rated_classnum,
'duration' => $cdr->rated_seconds,
diff --git a/FS/FS/detail_format/sum_count.pm b/FS/FS/detail_format/sum_count.pm
index c40fcb8fe..253956f0d 100644
--- a/FS/FS/detail_format/sum_count.pm
+++ b/FS/FS/detail_format/sum_count.pm
@@ -24,6 +24,7 @@ sub header_detail {
sub append {
my $self = shift;
my $svcnums = ($self->{svcnums} ||= {});
+ my $acctids = $self->{acctids} ||= [];
foreach my $cdr (@_) {
my $object = $self->{inbound} ? $cdr->cdr_termination(1) : $cdr;
my $svcnum = $object->svcnum; # yes, $object->svcnum.
@@ -33,6 +34,8 @@ sub append {
$subtotal->{count}++;
$subtotal->{amount} += $object->rated_price
if $object->freesidestatus ne 'no-charge';
+
+ push @$acctids, $cdr->acctid;
}
}
@@ -68,6 +71,7 @@ sub finish {
startdate => '', #could use the earliest startdate in the bunch?
regionname => '', #no, we're using prefix instead
detail => $self->csv->string,
+ acctid => $self->{acctids},
});
} #foreach $svcnum
diff --git a/FS/FS/detail_format/sum_duration.pm b/FS/FS/detail_format/sum_duration.pm
index 1b967b407..c41bed385 100644
--- a/FS/FS/detail_format/sum_duration.pm
+++ b/FS/FS/detail_format/sum_duration.pm
@@ -24,6 +24,7 @@ sub header_detail {
sub append {
my $self = shift;
my $svcnums = ($self->{svcnums} ||= {});
+ my $acctids = ($self->{acctids} ||= []);
foreach my $cdr (@_) {
my $object = $self->{inbound} ? $cdr->cdr_termination(1) : $cdr;
my $svcnum = $object->svcnum; # yes, $object->svcnum.
@@ -34,6 +35,8 @@ sub append {
$subtotal->{duration} += $object->rated_seconds;
$subtotal->{amount} += $object->rated_price
if $object->freesidestatus ne 'no-charge';
+
+ push @$acctids, $cdr->acctid;
}
}
@@ -70,6 +73,7 @@ sub finish {
startdate => '', #could use the earliest startdate in the bunch?
regionname => '', #no, we're using prefix instead
detail => $self->csv->string,
+ acctid => $self->{acctids},
});
} #foreach $svcnum
diff --git a/FS/FS/detail_format/sum_duration_prefix.pm b/FS/FS/detail_format/sum_duration_prefix.pm
index cd7bbe3cc..3c33dc163 100644
--- a/FS/FS/detail_format/sum_duration_prefix.pm
+++ b/FS/FS/detail_format/sum_duration_prefix.pm
@@ -24,6 +24,7 @@ my $prefix_length = 6;
sub append {
my $self = shift;
my $prefixes = ($self->{prefixes} ||= {});
+ my $acctids = ($self->{acctids} ||= []);
foreach my $cdr (@_) {
my (undef, $phonenum) = $cdr->parse_number(
column => ( $self->{inbound} ? 'src' : 'dst' ),
@@ -52,6 +53,8 @@ sub append {
$subtotal->{duration} += $object->rated_seconds;
$subtotal->{amount} += $object->rated_price
if $object->freesidestatus ne 'no-charge';
+
+ push @$acctids, $cdr->acctid;
}
}
@@ -91,6 +94,7 @@ sub finish {
startdate => '', #could use the earliest startdate in the bunch?
regionname => '', #no, we're using prefix instead
detail => $self->csv->string,
+ acctid => $self->{acctids},
});
} #foreach $prefix
}