summaryrefslogtreecommitdiff
path: root/FS/FS/part_pkg
diff options
context:
space:
mode:
Diffstat (limited to 'FS/FS/part_pkg')
-rw-r--r--FS/FS/part_pkg/agent.pm172
-rw-r--r--FS/FS/part_pkg/base_delayed.pm42
-rw-r--r--FS/FS/part_pkg/base_rate.pm83
-rw-r--r--FS/FS/part_pkg/bulk.pm130
-rw-r--r--FS/FS/part_pkg/cdr_termination.pm204
-rw-r--r--FS/FS/part_pkg/discount_Mixin.pm128
-rw-r--r--FS/FS/part_pkg/flat.pm204
-rw-r--r--FS/FS/part_pkg/flat_comission.pm60
-rw-r--r--FS/FS/part_pkg/flat_comission_cust.pm44
-rw-r--r--FS/FS/part_pkg/flat_comission_pkg.pm38
-rw-r--r--FS/FS/part_pkg/flat_delayed.pm54
-rw-r--r--FS/FS/part_pkg/flat_introrate.pm60
-rw-r--r--FS/FS/part_pkg/global_Mixin.pm38
-rw-r--r--FS/FS/part_pkg/incomplete/billoneday.pm48
-rw-r--r--FS/FS/part_pkg/prepaid.pm51
-rw-r--r--FS/FS/part_pkg/prorate.pm43
-rw-r--r--FS/FS/part_pkg/prorate_Mixin.pm105
-rw-r--r--FS/FS/part_pkg/prorate_delayed.pm53
-rw-r--r--FS/FS/part_pkg/recur_Common.pm70
-rw-r--r--FS/FS/part_pkg/rt_time.pm73
-rw-r--r--FS/FS/part_pkg/sesmon_hour.pm50
-rw-r--r--FS/FS/part_pkg/sesmon_minute.pm49
-rw-r--r--FS/FS/part_pkg/sql_external.pm77
-rw-r--r--FS/FS/part_pkg/sql_generic.pm81
-rw-r--r--FS/FS/part_pkg/sqlradacct_hour.pm163
-rw-r--r--FS/FS/part_pkg/subscription.pm108
-rw-r--r--FS/FS/part_pkg/usage_Mixin.pm77
-rw-r--r--FS/FS/part_pkg/voip_cdr.pm925
-rw-r--r--FS/FS/part_pkg/voip_inbound.pm366
-rw-r--r--FS/FS/part_pkg/voip_sqlradacct.pm185
30 files changed, 0 insertions, 3781 deletions
diff --git a/FS/FS/part_pkg/agent.pm b/FS/FS/part_pkg/agent.pm
deleted file mode 100644
index 6ab21d6..0000000
--- a/FS/FS/part_pkg/agent.pm
+++ /dev/null
@@ -1,172 +0,0 @@
-package FS::part_pkg::agent;
-
-use strict;
-use vars qw(@ISA $DEBUG $me %info);
-use Date::Format;
-use FS::Record qw( qsearch );
-use FS::agent;
-use FS::cust_main;
-
-#use FS::part_pkg::recur_Common;;
-#@ISA = qw(FS::part_pkg::recur_Common);
-use FS::part_pkg::prorate;
-@ISA = qw(FS::part_pkg::prorate);
-
-$DEBUG = 0;
-
-$me = '[FS::part_pkg::agent]';
-
-%info = (
- 'name' => 'Wholesale bulk billing, for master customers of an agent.',
- 'shortname' => 'Wholesale bulk billing for agent.',
- 'inherit_fields' => [qw( prorate global_Mixin)],
- 'fields' => {
- #'recur_method' => { 'name' => 'Recurring fee method',
- # #'type' => 'radio',
- # #'options' => \%recur_method,
- # 'type' => 'select',
- # 'select_options' => \%recur_Common::recur_method,
- # },
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28)',
- 'default' => '1',
- },
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
-
- 'no_pkg_prorate' => { 'name' => 'Disable prorating bulk packages (charge full price for packages active only a portion of the month)',
- 'type' => 'checkbox',
- },
-
- },
-
- 'fieldorder' => [qw( cutoff_day add_full_period no_pkg_prorate ) ],
-
- 'weight' => 51,
-
-);
-
-#some false laziness-ish w/bulk.pm... not a lot
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- my $last_bill = $cust_pkg->last_bill;
-
- return sprintf("%.2f", $self->SUPER::calc_recur(@_) )
- unless $$sdate > $last_bill;
-
- my $conf = new FS::Conf;
- my $money_char = $conf->config('money_char') || '$';
-
- my $total_agent_charge = 0;
-
- warn "$me billing for agent packages from ". time2str('%x', $last_bill).
- " to ". time2str('%x', $$sdate). "\n"
- if $DEBUG;
-
- my $prorate_ratio = ( $$sdate - $last_bill )
- / ( $self->add_freq($last_bill) - $last_bill );
-
- #almost always just one,
- #unless you have multiple agents with same master customer0
- my @agents = qsearch('agent', { 'agent_custnum' => $cust_pkg->custnum } );
-
- foreach my $agent (@agents) {
-
- warn "$me billing for agent ". $agent->agent. "\n"
- if $DEBUG;
-
- #not the most efficient to load them all into memory,
- #but good enough for our current needs
- my @cust_main = qsearch('cust_main', { 'agentnum' => $agent->agentnum } );
-
- foreach my $cust_main (@cust_main) {
-
- warn "$me billing agent charges for ". $cust_main->name_short. "\n"
- if $DEBUG;
-
- #make sure setup dates are filled in
- my $error = $cust_main->bill; #options don't propogate from freeside-daily
- die "Error pre-billing agent customer: $error" if $error;
-
- my @cust_pkg = grep { my $setup = $_->get('setup');
- my $cancel = $_->get('cancel');
-
- $setup < $$sdate # END
- && ( ! $cancel || $cancel > $last_bill ) #START
- }
- $cust_main->all_pkgs;
-
- foreach my $cust_pkg ( @cust_pkg ) {
-
- warn "$me billing agent charges for pkgnum ". $cust_pkg->pkgnum. "\n"
- if $DEBUG;
-
- my $pkg_details = $cust_main->name_short. ': '; #name?
- # + something to identify package... primary service probably
-
- my $pkg_charge = 0;
-
- my $part_pkg = $cust_pkg->part_pkg;
- #option to not fallback? via options above
- my $pkg_setup_fee =
- $part_pkg->setup_cost || $part_pkg->option('setup_fee');
- my $pkg_base_recur =
- $part_pkg->recur_cost || $part_pkg->base_recur_permonth($cust_pkg);
-
- my $pkg_start = $cust_pkg->get('setup');
- if ( $pkg_start < $last_bill ) {
- $pkg_start = $last_bill;
- } elsif ( $pkg_setup_fee ) {
- $pkg_charge += $pkg_setup_fee;
- $pkg_details .= $money_char. sprintf('%.2f setup, ', $pkg_setup_fee );
- }
-
- my $pkg_end = $cust_pkg->get('cancel');
- $pkg_end = ( !$pkg_end || $pkg_end > $$sdate ) ? $$sdate : $pkg_end;
-
-
- my $pkg_recur_charge = $prorate_ratio * $pkg_base_recur;
- $pkg_recur_charge *= ( $pkg_end - $pkg_start )
- / ( $$sdate - $last_bill )
- unless $self->option('no_pkg_prorate');
-
- my $recur_charge += $pkg_recur_charge;
-
- $pkg_details .= $money_char. sprintf('%.2f', $recur_charge ).
- ' ('. time2str('%x', $pkg_start).
- ' - '. time2str('%x', $pkg_end ). ')'
- if $recur_charge;
-
- $pkg_charge += $recur_charge;
-
- push @$details, $pkg_details
- if $pkg_charge;
- $total_agent_charge += $pkg_charge;
-
- } #foreach $cust_pkg
-
- } #foreach $cust_main
-
- } #foreach $agent;
-
- my $charges = $total_agent_charge + $self->SUPER::calc_recur(@_); #prorate
-
- sprintf('%.2f', $charges );
-
-}
-
-sub can_discount { 0; }
-
-sub hide_svc_detail {
- 1;
-}
-
-sub is_free {
- 0;
-}
-
-1;
-
diff --git a/FS/FS/part_pkg/base_delayed.pm b/FS/FS/part_pkg/base_delayed.pm
deleted file mode 100644
index c6864a6..0000000
--- a/FS/FS/part_pkg/base_delayed.pm
+++ /dev/null
@@ -1,42 +0,0 @@
-package FS::part_pkg::base_delayed;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::base_rate;
-
-@ISA = qw(FS::part_pkg::base_rate);
-
-%info = (
- 'name' => 'Free (or setup fee) for X days, then base rate'.
- ' (anniversary billing)',
- 'shortname' => 'Bulk (manual from "units" option), w/intro period',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'free_days' => { 'name' => 'Initial free days',
- 'default' => 0,
- },
- 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
- ' commences to notify customer. (0 means'.
- ' no warning)',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ 'free_days', 'recur_notify',
- ],
- #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
- #'recur' => 'what.recur_fee.value',
- 'weight' => 54, #&g!
-);
-
-sub calc_setup {
- my($self, $cust_pkg, $time ) = @_;
-
- my $d = $cust_pkg->bill || $time;
- $d += 86400 * $self->option('free_days');
- $cust_pkg->bill($d);
-
- $self->option('setup_fee');
-}
-
-1;
diff --git a/FS/FS/part_pkg/base_rate.pm b/FS/FS/part_pkg/base_rate.pm
deleted file mode 100644
index 6781977..0000000
--- a/FS/FS/part_pkg/base_rate.pm
+++ /dev/null
@@ -1,83 +0,0 @@
-package FS::part_pkg::base_rate;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch);
-use FS::part_pkg;
-
-@ISA = qw(FS::part_pkg);
-
-%info = (
- 'name' => 'Base rate (anniversary billing, Times units ordered)',
- # XXX it multiplies recurring fee by cust_pkg option "units", how to
- # express that
- 'shortname' => 'Bulk (manual from "units" option)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'externalid' => { 'name' => 'Optional External ID',
- 'default' => '',
- },
- },
- 'fieldorder' => [ qw( externalid ) ],
- 'weight' => 52,
-);
-
-sub calc_setup {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- my $i = 0;
- my $count = $self->option( 'additional_count', 'quiet' ) || 0;
- while ($i < $count) {
- push @$details, $self->option( 'additional_info' . $i++ );
- }
-
- $self->option('setup_fee');
-}
-
-sub calc_recur {
- my($self, $cust_pkg) = @_;
- $self->base_recur($cust_pkg);
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- my $units = $cust_pkg->option('units') ? $cust_pkg->option('units') : 1 ;
- # default to 1 if not found
- sprintf("%.2f",
- ($self->option('recur_fee') * $units )
- );
-}
-
-sub calc_remain {
- my ($self, $cust_pkg, %options) = @_;
- my $time = $options{'time'} || time;
- my $next_bill = $cust_pkg->getfield('bill') || 0;
- return 0 if ! $self->base_recur($cust_pkg)
- || ! $next_bill
- || $next_bill < $time;
-
- my %sec = (
- 'h' => 3600, # 60 * 60
- 'd' => 86400, # 60 * 60 * 24
- 'w' => 604800, # 60 * 60 * 24 * 7
- 'm' => 2629744, # 60 * 60 * 24 * 365.2422 / 12
- );
-
- $self->freq =~ /^(\d+)([hdwm]?)$/
- or die 'unparsable frequency: '. $self->freq;
- my $freq_sec = $1 * $sec{$2||'m'};
- return 0 unless $freq_sec;
-
- sprintf("%.2f", $self->base_recur($cust_pkg) * ( $next_bill - $time ) / $freq_sec );
-
-}
-
-sub is_free_options {
- qw( setup_fee recur_fee );
-}
-
-sub is_prepaid {
- 0; #no, we're postpaid
-}
-
-1;
diff --git a/FS/FS/part_pkg/bulk.pm b/FS/FS/part_pkg/bulk.pm
deleted file mode 100644
index 0df929e..0000000
--- a/FS/FS/part_pkg/bulk.pm
+++ /dev/null
@@ -1,130 +0,0 @@
-package FS::part_pkg::bulk;
-
-use strict;
-use vars qw(@ISA $DEBUG $me %info);
-use Date::Format;
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-$DEBUG = 0;
-$me = '[FS::part_pkg::bulk]';
-
-%info = (
- 'name' => 'Bulk billing based on number of active services',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'svc_setup_fee' => { 'name' => 'Setup fee for each new service',
- 'default' => 0,
- },
- 'svc_recur_fee' => { 'name' => 'Recurring fee for each service',
- 'default' => 0,
- },
- 'summarize_svcs'=> { 'name' => 'Show a count of services on the invoice, '.
- 'instead of a detailed list',
- 'type' => 'checkbox',
- },
- 'no_prorate' => { 'name' => 'Don\'t prorate recurring fees on services '.
- 'active for a partial month',
- 'type' => 'checkbox',
- },
- },
- 'fieldorder' => [ 'svc_setup_fee', 'svc_recur_fee',
- 'summarize_svcs', 'no_prorate' ],
- 'weight' => 50,
-);
-
-#some false laziness-ish w/agent.pm... not a lot
-sub calc_recur {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- my $conf = new FS::Conf;
- my $money_char = $conf->config('money_char') || '$';
-
- my $svc_setup_fee = $self->option('svc_setup_fee');
-
- my $last_bill = $cust_pkg->last_bill;
-
- return sprintf("%.2f", $self->base_recur($cust_pkg) )
- unless $$sdate > $last_bill;
-
- my $total_svc_charge = 0;
- my %n_setup = ();
- my %n_recur = ();
- my %part_svc_label = ();
-
- my $summarize = $self->option('summarize_svcs',1);
-
- warn "$me billing for bulk services from ". time2str('%x', $last_bill).
- " to ". time2str('%x', $$sdate). "\n"
- if $DEBUG;
-
- # END START
- foreach my $h_cust_svc ( $cust_pkg->h_cust_svc( $$sdate, $last_bill ) ) {
-
- my @label = $h_cust_svc->label_long( $$sdate, $last_bill );
- die "fatal: no historical label found, wtf?" unless scalar(@label); #?
- my $svc_details = $label[0]. ': '. $label[1]. ': ';
- $part_svc_label{$h_cust_svc->svcpart} ||= $label[0];
-
- my $svc_charge = 0;
-
- my $svc_start = $h_cust_svc->date_inserted;
- if ( $svc_start < $last_bill ) {
- $svc_start = $last_bill;
- } elsif ( $svc_setup_fee ) {
- $svc_charge += $svc_setup_fee;
- $svc_details .= $money_char. sprintf('%.2f setup, ', $svc_setup_fee);
- $n_setup{$h_cust_svc->svcpart}++;
- }
-
- my $svc_end = $h_cust_svc->date_deleted;
- $svc_end = ( !$svc_end || $svc_end > $$sdate ) ? $$sdate : $svc_end;
-
- my $recur_charge;
- if ( $self->option('no_prorate',1) ) {
- $recur_charge = $self->option('svc_recur_fee');
- }
- else {
- $recur_charge = $self->option('svc_recur_fee')
- * ( $svc_end - $svc_start )
- / ( $$sdate - $last_bill );
- }
-
- $svc_details .= $money_char. sprintf('%.2f', $recur_charge ).
- ' ('. time2str('%x', $svc_start).
- ' - '. time2str('%x', $svc_end ). ')'
- if $recur_charge;
-
- $svc_charge += $recur_charge;
- $n_recur{$h_cust_svc->svcpart}++;
- push @$details, $svc_details if !$summarize;
- $total_svc_charge += $svc_charge;
-
- }
- if ( $summarize ) {
- foreach my $svcpart (keys %part_svc_label) {
- push @$details, sprintf('Setup fee: %d @ '.$money_char.'%.2f',
- $n_setup{$svcpart}, $svc_setup_fee )
- if $svc_setup_fee and $n_setup{$svcpart};
- push @$details, sprintf('%d services @ '.$money_char.'%.2f',
- $n_recur{$svcpart}, $self->option('svc_recur_fee') )
- if $n_recur{$svcpart};
- }
- }
-
- sprintf('%.2f', $self->base_recur($cust_pkg) + $total_svc_charge );
-}
-
-sub can_discount { 0; }
-
-sub hide_svc_detail {
- 1;
-}
-
-sub is_free_options {
- qw( setup_fee recur_fee svc_setup_fee svc_recur_fee );
-}
-
-1;
-
diff --git a/FS/FS/part_pkg/cdr_termination.pm b/FS/FS/part_pkg/cdr_termination.pm
deleted file mode 100644
index 840da82..0000000
--- a/FS/FS/part_pkg/cdr_termination.pm
+++ /dev/null
@@ -1,204 +0,0 @@
-package FS::part_pkg::cdr_termination;
-
-use strict;
-use base qw( FS::part_pkg::recur_Common );
-use vars qw( $DEBUG %info );
-use Tie::IxHash;
-use FS::Record qw( qsearch ); #qsearchs );
-use FS::cdr;
-use FS::cdr_termination;
-
-tie my %temporalities, 'Tie::IxHash',
- 'upcoming' => "Upcoming (future)",
- 'preceding' => "Preceding (past)",
-;
-
-%info = (
- 'name' => 'VoIP rating of CDR records for termination partners.',
- 'shortname' => 'VoIP/telco CDR termination',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- #'cdr_column' => { 'name' => 'Column from CDR records',
- # 'type' => 'select',
- # 'select_enum' => [qw(
- # dcontext
- # channel
- # dstchannel
- # lastapp
- # lastdata
- # accountcode
- # userfield
- # cdrtypenum
- # calltypenum
- # description
- # carrierid
- # upstream_rateid
- # )],
- # },
-
- #false laziness w/flat.pm
- 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
- 'type' => 'select',
- 'select_options' => \%temporalities,
- },
-
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28) for prorating or '.
- 'subscription',
- 'default' => '1',
- },
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
-
- 'recur_method' => { 'name' => 'Recurring fee method',
- #'type' => 'radio',
- #'options' => \%recur_method,
- 'type' => 'select',
- 'select_options' => \%FS::part_pkg::recur_Common::recur_method,
- },
-
- #false laziness w/voip_cdr.pm
- 'output_format' => { 'name' => 'CDR invoice display format',
- 'type' => 'select',
- 'select_options' => { FS::cdr::invoice_formats() },
- 'default' => 'simple2', #XXX test
- },
-
- 'usage_section' => { 'name' => 'Section in which to place separate usage charges',
- },
-
- 'summarize_usage' => { 'name' => 'Include usage summary with recurring charges when usage is in separate section',
- 'type' => 'checkbox',
- },
-
- 'usage_mandate' => { 'name' => 'Always put usage details in separate section',
- 'type' => 'checkbox',
- },
- #eofalse
-
- },
- #cdr_column
- 'fieldorder' => [qw(
- recur_temporality recur_method cutoff_day
- add_full_period
- output_format usage_section summarize_usage usage_mandate
- )
- ],
-
- 'weight' => 48,
-
-);
-
-sub calc_setup {
- my($self, $cust_pkg ) = @_;
- $self->option('setup_fee');
-}
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- #my $last_bill = $cust_pkg->last_bill;
- my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
-
- return 0
- if $self->option('recur_temporality', 1) eq 'preceding'
- && ( $last_bill eq '' || $last_bill == 0 );
-
- # termination calculations
-
- my $term_percent = $cust_pkg->cust_main->cdr_termination_percentage;
- die "no customer termination percentage" unless $term_percent;
-
- my $output_format = $self->option('output_format', 'Hush!') || 'simple2';
-
- my $charges = 0;
-
- #find an svc_external record
- my @svc_external = map { $_->svc_x }
- grep { $_->part_svc->svcdb eq 'svc_external' }
- $cust_pkg->cust_svc;
-
- die "cdr_termination package has no svc_external service"
- unless @svc_external;
- die "cdr_termination package has multiple svc_external services"
- if scalar(@svc_external) > 1;
-
- my $svc_external = $svc_external[0];
-
- # find CDRs:
- # - matching our customer via svc_external.id/title? (and via what field?)
-
- #let's try carrierid for now, can always make it configurable or rewrite
- my $cdr_column = 'carrierid';
-
- my %hashref = ( 'freesidestatus' => 'done' );
-
- # try matching on svc_external.id for now... (or title? if ints don't cut it)
- $hashref{$cdr_column} = $svc_external[0]->id;
-
- # - with no cdr_termination.status
-
- my $termpart = 1; #or from an option
-
- #false lazienss w/search/cdr.html (i should be a part_termination method)
- my $where_term =
- "( cdr.acctid = cdr_termination.acctid AND termpart = $termpart ) ";
- #my $join_term = "LEFT JOIN cdr_termination ON ( $where_term )";
- my $extra_sql =
- "AND NOT EXISTS ( SELECT 1 FROM cdr_termination WHERE $where_term )";
-
- #may need to process in batches if there's waaay too many
- my @cdrs = qsearch({
- 'table' => 'cdr',
- #'addl_from' => $join_term,
- 'hashref' => \%hashref,
- 'extra_sql' => "$extra_sql FOR UPDATE",
- });
-
- foreach my $cdr (@cdrs) {
-
- #add a cdr_termination record and the charges
-
- # XXX config?
- #my $term_price = sprintf('%.2f', $cdr->rated_price * $term_percent / 100 );
- my $term_price = sprintf('%.4f', $cdr->rated_price * $term_percent / 100 );
-
- my $cdr_termination = new FS::cdr_termination {
- 'acctid' => $cdr->acctid,
- 'termpart' => $termpart,
- 'rated_price' => $term_price,
- 'status' => 'done',
- };
-
- my $error = $cdr_termination->insert;
- die $error if $error; #next if $error; #or just skip this one??? why?
-
- $charges += $term_price;
-
- # and add a line to the invoice
-
- my $call_details = $cdr->downstream_csv( 'format' => $output_format,
- 'charge' => $term_price,
- );
-
- my $classnum = ''; #usage class?
-
- #option to turn off? or just use squelch_cdr for the customer probably
- push @$details, [ 'C', $call_details, $term_price, $classnum ];
-
- }
-
- # eotermiation calculation
-
- $charges += $self->calc_recur_Common(@_);
-
- $charges;
-}
-
-sub is_free {
- 0;
-}
-
-1;
diff --git a/FS/FS/part_pkg/discount_Mixin.pm b/FS/FS/part_pkg/discount_Mixin.pm
deleted file mode 100644
index df65e97..0000000
--- a/FS/FS/part_pkg/discount_Mixin.pm
+++ /dev/null
@@ -1,128 +0,0 @@
-package FS::part_pkg::discount_Mixin;
-
-use strict;
-use vars qw(@ISA %info);
-use FS::part_pkg;
-use FS::cust_pkg;
-use FS::cust_bill_pkg_discount;
-use Time::Local qw(timelocal);
-use List::Util 'min';
-
-@ISA = qw(FS::part_pkg);
-%info = ( 'disabled' => 1 );
-
-=head1 NAME
-
-FS::part_pkg::discount_Mixin - Mixin class for part_pkg:: classes that
-can be discounted.
-
-=head1 SYNOPSIS
-
-package FS::part_pkg::...;
-use base qw( FS::part_pkg::discount_Mixin );
-
-sub calc_recur {
- ...
- my $discount = $self->calc_discount($cust_pkg, $$sdate, $details, $param);
- $charge -= $discount;
- ...
-}
-
-=head METHODS
-
-=item calc_discount
-
-Takes all the arguments of calc_recur. Calculates and returns the amount
-by which to reduce the recurring fee; also increments months used on the
-discount and generates an invoice detail describing it.
-
-=cut
-
-sub calc_discount {
- my($self, $cust_pkg, $sdate, $details, $param ) = @_;
-
- my $br = $self->base_recur($cust_pkg);
-
- my $tot_discount = 0;
- #UI enforces just 1 for now, will need ordering when they can be stacked
-
- if ( $param->{freq_override} ) {
- # When a customer pays for more than one month at a time to receive a
- # term discount, freq_override is set to the number of months.
- my $real_part_pkg = new FS::part_pkg { $self->hash };
- $real_part_pkg->pkgpart($param->{real_pkgpart} || $self->pkgpart);
- # Find a discount with that duration...
- my @discount = grep { $_->months == $param->{freq_override} }
- map { $_->discount } $real_part_pkg->part_pkg_discount;
- my $discount = shift @discount;
- # and default to bill that many months at once.
- $param->{months} = $param->{freq_override} unless $param->{months};
- my $error;
- if ($discount) {
- # Then set the cust_pkg discount.
- if ($discount->months == $param->{months}) {
- $cust_pkg->discountnum($discount->discountnum);
- $error = $cust_pkg->insert_discount;
- } else {
- $cust_pkg->discountnum(-1);
- foreach ( qw( amount percent months ) ) {
- my $method = "discountnum_$_";
- $cust_pkg->$method($discount->$_);
- }
- $error = $cust_pkg->insert_discount;
- }
- die "error discounting using part_pkg_discount: $error" if $error;
- }
- }
-
- my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
- foreach my $cust_pkg_discount ( @cust_pkg_discount ) {
- my $discount = $cust_pkg_discount->discount;
- #UI enforces one or the other (for now? probably for good)
- my $amount = 0;
- $amount += $discount->amount
- if $cust_pkg->pkgpart == $param->{real_pkgpart};
- $amount += sprintf('%.2f', $discount->percent * $br / 100 );
- my $chg_months = $param->{'months'} || $cust_pkg->part_pkg->freq;
-
- my $months = $discount->months
- ? min( $chg_months,
- $discount->months - $cust_pkg_discount->months_used )
- : $chg_months;
-
- my $error = $cust_pkg_discount->increment_months_used($months)
- if $cust_pkg->pkgpart == $param->{real_pkgpart};
- die "error discounting: $error" if $error;
-
- $amount *= $months;
- $amount = sprintf('%.2f', $amount);
-
- next unless $amount > 0;
-
- #record details in cust_bill_pkg_discount
- my $cust_bill_pkg_discount = new FS::cust_bill_pkg_discount {
- 'pkgdiscountnum' => $cust_pkg_discount->pkgdiscountnum,
- 'amount' => $amount,
- 'months' => $months,
- };
- push @{ $param->{'discounts'} }, $cust_bill_pkg_discount;
-
- #add details on discount to invoice
- my $conf = new FS::Conf;
- my $money_char = $conf->config('money_char') || '$';
- $months = sprintf('%.2f', $months) if $months =~ /\./;
-
- my $d = 'Includes ';
- $d .= $discount->name. ' ' if $discount->name;
- $d .= 'discount of '. $discount->description_short;
- $d .= " for $months month". ( $months!=1 ? 's' : '' );
- $d .= ": $money_char$amount" if $months != 1 || $discount->percent;
- push @$details, $d;
-
- $tot_discount += $amount;
- }
-
- sprintf('%.2f', $tot_discount);
-}
-
-1;
diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
deleted file mode 100644
index b5e0fa0..0000000
--- a/FS/FS/part_pkg/flat.pm
+++ /dev/null
@@ -1,204 +0,0 @@
-package FS::part_pkg::flat;
-
-use strict;
-use vars qw( @ISA %info
- %usage_recharge_fields @usage_recharge_fieldorder
- );
-use Tie::IxHash;
-use List::Util qw(min); # max);
-#use FS::Record qw(qsearch);
-use FS::UI::bytecount;
-use FS::Conf;
-use FS::part_pkg;
-
-@ISA = qw(FS::part_pkg
- FS::part_pkg::prorate_Mixin
- FS::part_pkg::discount_Mixin);
-
-tie my %temporalities, 'Tie::IxHash',
- 'upcoming' => "Upcoming (future)",
- 'preceding' => "Preceding (past)",
-;
-
-tie my %contract_years, 'Tie::IxHash', (
- '' => '(none)',
- map { $_*12 => $_ } (1..5),
-);
-
-%info = (
- 'name' => 'Flat rate (anniversary billing)',
- 'shortname' => 'Anniversary',
- 'inherit_fields' => [ 'usage_Mixin', 'global_Mixin' ],
- 'fields' => {
- #false laziness w/voip_cdr.pm
- 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
- 'type' => 'select',
- 'select_options' => \%temporalities,
- },
-
- #used in cust_pkg.pm so could add to any price plan
- 'expire_months' => { 'name' => 'Auto-add an expiration date this number of months out',
- },
- 'adjourn_months'=> { 'name' => 'Auto-add a suspension date this number of months out',
- },
- 'contract_end_months'=> {
- 'name' => 'Auto-add a contract end date this number of years out',
- 'type' => 'select',
- 'select_options' => \%contract_years,
- },
- #used in cust_pkg.pm so could add to any price plan where it made sense
- 'start_1st' => { 'name' => 'Auto-add a start date to the 1st, ignoring the current month.',
- 'type' => 'checkbox',
- },
- 'sync_bill_date' => { 'name' => 'Prorate first month to synchronize '.
- 'with the customer\'s other packages',
- 'type' => 'checkbox',
- },
- 'suspend_bill' => { 'name' => 'Continue recurring billing while suspended',
- 'type' => 'checkbox',
- },
- 'unsuspend_adjust_bill' =>
- { 'name' => 'Adjust next bill date forward when '.
- 'unsuspending',
- 'type' => 'checkbox',
- },
-
- 'externalid' => { 'name' => 'Optional External ID',
- 'default' => '',
- },
- },
- 'fieldorder' => [ qw( recur_temporality
- expire_months adjourn_months
- contract_end_months
- start_1st sync_bill_date
- suspend_bill unsuspend_adjust_bill
- externalid ),
- ],
- 'weight' => 10,
-);
-
-sub calc_setup {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- my $i = 0;
- my $count = $self->option( 'additional_count', 'quiet' ) || 0;
- while ($i < $count) {
- push @$details, $self->option( 'additional_info' . $i++ );
- }
-
- my $quantity = $cust_pkg->quantity || 1;
-
- sprintf("%.2f", $quantity * $self->unit_setup($cust_pkg, $sdate, $details) );
-}
-
-sub unit_setup {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- $self->option('setup_fee') || 0;
-}
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- #my $last_bill = $cust_pkg->last_bill;
- my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
-
- return 0
- if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
-
- my $charge = $self->base_recur($cust_pkg);
- if ( $self->option('sync_bill_date',1) ) {
- my $next_bill = $cust_pkg->cust_main->next_bill_date;
- if ( defined($next_bill) ) {
- my $cutoff_day = (localtime($next_bill))[3];
- $charge = $self->calc_prorate(@_, $cutoff_day);
- }
- }
- elsif ( $param->{freq_override} ) {
- # XXX not sure if this should be mutually exclusive with sync_bill_date.
- # Given the very specific problem that freq_override is meant to 'solve',
- # it probably should.
- $charge *= $param->{freq_override} if $param->{freq_override};
- }
-
- my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
- return sprintf('%.2f', $charge - $discount);
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee', 1) || 0;
-}
-
-sub base_recur_permonth {
- my($self, $cust_pkg) = @_;
-
- return 0 unless $self->freq =~ /^\d+$/ && $self->freq > 0;
-
- sprintf('%.2f', $self->base_recur($cust_pkg) / $self->freq );
-}
-
-sub calc_remain {
- my ($self, $cust_pkg, %options) = @_;
-
- my $time;
- if ($options{'time'}) {
- $time = $options{'time'};
- } else {
- $time = time;
- }
-
- my $next_bill = $cust_pkg->getfield('bill') || 0;
-
- return 0 if ! $self->base_recur($cust_pkg)
- || ! $next_bill
- || $next_bill < $time;
-
- my %sec = (
- 'h' => 3600, # 60 * 60
- 'd' => 86400, # 60 * 60 * 24
- 'w' => 604800, # 60 * 60 * 24 * 7
- 'm' => 2629744, # 60 * 60 * 24 * 365.2422 / 12
- );
-
- $self->freq =~ /^(\d+)([hdwm]?)$/
- or die 'unparsable frequency: '. $self->freq;
- my $freq_sec = $1 * $sec{$2||'m'};
- return 0 unless $freq_sec;
-
- sprintf("%.2f", $self->base_recur($cust_pkg) * ( $next_bill - $time ) / $freq_sec );
-
-}
-
-sub is_free_options {
- qw( setup_fee recur_fee );
-}
-
-sub is_prepaid { 0; } #no, we're postpaid
-
-#XXX discounts only on recurring fees for now (no setup/one-time or usage)
-sub can_discount {
- my $self = shift;
- $self->freq =~ /^\d+$/ && $self->freq > 0;
-}
-
-sub usage_valuehash {
- my $self = shift;
- map { $_, $self->option($_) }
- grep { $self->option($_, 'hush') }
- qw(seconds upbytes downbytes totalbytes);
-}
-
-sub reset_usage {
- my($self, $cust_pkg, %opt) = @_;
- warn " resetting usage counters" if defined($opt{debug}) && $opt{debug} > 1;
- my %values = $self->usage_valuehash;
- if ($self->option('usage_rollover', 1)) {
- $cust_pkg->recharge(\%values);
- }else{
- $cust_pkg->set_usage(\%values, %opt);
- }
-}
-
-1;
diff --git a/FS/FS/part_pkg/flat_comission.pm b/FS/FS/part_pkg/flat_comission.pm
deleted file mode 100644
index ec8c8eb..0000000
--- a/FS/FS/part_pkg/flat_comission.pm
+++ /dev/null
@@ -1,60 +0,0 @@
-package FS::part_pkg::flat_comission;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Flat rate with recurring commission per (any) active package',
- 'shortname' => 'Commission per (any) active package',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'comission_amount' => { 'name' => 'Commission amount per month (per active package)',
- 'default' => 0,
- },
- 'comission_depth' => { 'name' => 'Number of layers',
- 'default' => 1,
- },
- 'reason_type' => { 'name' => 'Reason type for commission credits',
- 'type' => 'select',
- 'select_table' => 'reason_type',
- 'select_hash' => { 'class' => 'R' },
- 'select_key' => 'typenum',
- 'select_label' => 'type',
- },
- },
- 'fieldorder' => [ 'comission_depth', 'comission_amount', 'reason_type' ],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
- 'weight' => 62,
-);
-
-sub calc_recur {
- my($self, $cust_pkg ) = @_;
-
- my $amount = $self->option('comission_amount');
- my $num_active = scalar(
- $cust_pkg->cust_main->referral_cust_pkg( $self->option('comission_depth') )
- );
-
- my $commission = sprintf('%.2f', $amount*$num_active);
-
- if ( $commission > 0 ) {
-
- my $error =
- $cust_pkg->cust_main->credit( $commission, "commission",
- 'reason_type'=>$self->option('reason_type'),
- );
- die $error if $error;
-
- }
-
- $self->option('recur_fee');
-}
-
-sub can_discount { 0; }
-
-1;
diff --git a/FS/FS/part_pkg/flat_comission_cust.pm b/FS/FS/part_pkg/flat_comission_cust.pm
deleted file mode 100644
index 5acf73d..0000000
--- a/FS/FS/part_pkg/flat_comission_cust.pm
+++ /dev/null
@@ -1,44 +0,0 @@
-package FS::part_pkg::flat_comission_cust;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Flat rate with recurring commission per active customer',
- 'shortname' => 'Commission per active customer',
- 'inherit_fields' => [ 'flat_comission', 'global_Mixin' ],
- 'fields' => { },
- 'fieldorder' => [ ],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_main_ncancelled(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
- 'weight' => '60',
-);
-
-sub calc_recur {
- my($self, $cust_pkg ) = @_;
-
- my $amount = $self->option('comission_amount');
- my $num_active = scalar(
- $cust_pkg->cust_main->referral_cust_main_ncancelled(
- $self->option('comission_depth')
- )
- );
-
- if ( $amount && $num_active ) {
- my $error =
- $cust_pkg->cust_main->credit( $amount*$num_active, "commission",
- 'reason_type'=>$self->option('reason_type'),
- );
- die $error if $error;
- }
-
- $self->option('recur_fee');
-}
-
-sub can_discount { 0; }
-
-1;
diff --git a/FS/FS/part_pkg/flat_comission_pkg.pm b/FS/FS/part_pkg/flat_comission_pkg.pm
deleted file mode 100644
index 26dd4d2..0000000
--- a/FS/FS/part_pkg/flat_comission_pkg.pm
+++ /dev/null
@@ -1,38 +0,0 @@
-package FS::part_pkg::flat_comission_pkg;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Flat rate with recurring commission per (selected) active package',
- 'shortname' => 'Commission per (selected) active package',
- 'inherit_fields' => [ 'flat_comission', 'global_Mixin' ],
- 'fields' => {
- 'comission_pkgpart' => { 'name' => 'Applicable packages<BR><FONT SIZE="-1">(hold <b>ctrl</b> to select multiple packages)</FONT>',
- 'type' => 'select_multiple',
- 'select_table' => 'part_pkg',
- 'select_hash' => { 'disabled' => '' } ,
- 'select_key' => 'pkgpart',
- 'select_label' => 'pkg',
- },
- },
- 'fieldorder' => [ 'comission_depth', 'comission_amount', 'comission_pkgpart', 'reason_type' ],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '""; var pkgparts = ""; for ( var c=0; c < document.flat_comission_pkg.comission_pkgpart.options.length; c++ ) { if (document.flat_comission_pkg.comission_pkgpart.options[c].selected) { pkgparts = pkgparts + document.flat_comission_pkg.comission_pkgpart.options[c].value + \', \'; } } what.recur.value = \'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar( grep { my $pkgpart = $_->pkgpart; grep { $_ == $pkgpart } ( \' + pkgparts + \' ) } $cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
- #'disabled' => 1,
- 'weight' => '64',
-);
-
-# XXX this needs to be fixed!!!
-sub calc_recur {
- my($self, $cust_pkg ) = @_;
- $self->option('recur_fee');
-}
-
-sub can_discount { 0; }
-
-1;
diff --git a/FS/FS/part_pkg/flat_delayed.pm b/FS/FS/part_pkg/flat_delayed.pm
deleted file mode 100644
index b4be72b..0000000
--- a/FS/FS/part_pkg/flat_delayed.pm
+++ /dev/null
@@ -1,54 +0,0 @@
-package FS::part_pkg::flat_delayed;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Free (or setup fee) for X days, then flat rate'.
- ' (anniversary billing)',
- 'shortname' => 'Anniversary, with intro period',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'free_days' => { 'name' => 'Initial free days',
- 'default' => 0,
- },
- 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
- ' commences to notify customer. (0 means'.
- ' no warning)',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ 'free_days', 'recur_notify',
- ],
- #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
- #'recur' => 'what.recur_fee.value',
- 'weight' => 12,
-);
-
-sub calc_setup {
- my($self, $cust_pkg, $time ) = @_;
-
- my $d = $cust_pkg->bill || $time;
- $d += 86400 * $self->option('free_days');
- $cust_pkg->bill($d);
-
- $self->option('setup_fee');
-}
-
-sub calc_remain {
- my ($self, $cust_pkg, %options) = @_;
- my $next_bill = $cust_pkg->getfield('bill') || 0;
- my $last_bill = $cust_pkg->last_bill || 0;
- my $free_days = $self->option('free_days');
-
- return 0 if $last_bill + (86400 * $free_days) == $next_bill
- && $last_bill == $cust_pkg->setup;
-
- return $self->SUPER::calc_remain($cust_pkg, %options);
-}
-
-1;
diff --git a/FS/FS/part_pkg/flat_introrate.pm b/FS/FS/part_pkg/flat_introrate.pm
deleted file mode 100644
index 1447730..0000000
--- a/FS/FS/part_pkg/flat_introrate.pm
+++ /dev/null
@@ -1,60 +0,0 @@
-package FS::part_pkg::flat_introrate;
-
-use strict;
-use vars qw(@ISA %info $DEBUG $me);
-use FS::part_pkg::flat;
-
-use Date::Manip qw(DateCalc UnixDate ParseDate);
-
-@ISA = qw(FS::part_pkg::flat);
-$me = '[' . __PACKAGE__ . ']';
-$DEBUG = 0;
-
-%info = (
- 'name' => 'Introductory price for X months, then flat rate,'.
- 'relative to setup date (anniversary billing)',
- 'shortname' => 'Anniversary, with intro price',
- 'inherit_fields' => [ 'flat', 'usage_Mixin', 'global_Mixin' ],
- 'fields' => {
- 'intro_fee' => { 'name' => 'Introductory recurring fee for this package',
- 'default' => 0,
- },
- 'intro_duration' =>
- { 'name' => 'Duration of the introductory period, in number of months',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ qw(intro_duration intro_fee) ],
- 'weight' => 14,
-);
-
-sub base_recur {
- my($self, $cust_pkg, $time ) = @_;
-
- my $now = $time ? $$time : time;
-
- my ($duration) = ($self->option('intro_duration') =~ /^(\d+)$/);
- unless ($duration) {
- die "Invalid intro_duration: " . $self->option('intro_duration');
- }
-
- my $setup = &ParseDate('epoch ' . $cust_pkg->getfield('setup'));
- my $intro_end = &DateCalc($setup, "+${duration} month");
- my $recur;
-
- warn "$me: \$duration = ${duration}" if $DEBUG;
- warn "$me: \$intro_end = ${intro_end}" if $DEBUG;
- warn "$me: $now < " . &UnixDate($intro_end, '%s') if $DEBUG;
-
- if ($now < &UnixDate($intro_end, '%s')) {
- $recur = $self->option('intro_fee');
- } else {
- $recur = $self->option('recur_fee');
- }
-
- $recur;
-
-}
-
-
-1;
diff --git a/FS/FS/part_pkg/global_Mixin.pm b/FS/FS/part_pkg/global_Mixin.pm
deleted file mode 100644
index 56f1602..0000000
--- a/FS/FS/part_pkg/global_Mixin.pm
+++ /dev/null
@@ -1,38 +0,0 @@
-package FS::part_pkg::global_Mixin;
-
-use strict;
-use vars qw(@ISA %info);
-use FS::part_pkg;
-@ISA = qw(FS::part_pkg);
-
-%info = (
- 'disabled' => 1,
- 'fields' => {
- 'setup_fee' => {
- 'name' => 'Setup fee for this package',
- 'default' => 0,
- },
- 'recur_fee' => {
- 'name' => 'Recurring fee for this package',
- 'default' => 0,
- },
- 'unused_credit_cancel' => {
- 'name' => 'Credit the customer for the unused portion of service at '.
- 'cancellation',
- 'type' => 'checkbox',
- },
- 'unused_credit_change' => {
- 'name' => 'Credit the customer for the unused portion of service when '.
- 'changing packages',
- 'type' => 'checkbox',
- },
- },
- 'fieldorder' => [ qw(
- setup_fee
- recur_fee
- unused_credit_cancel
- unused_credit_change
- )],
-);
-
-1;
diff --git a/FS/FS/part_pkg/incomplete/billoneday.pm b/FS/FS/part_pkg/incomplete/billoneday.pm
deleted file mode 100644
index 8740547..0000000
--- a/FS/FS/part_pkg/incomplete/billoneday.pm
+++ /dev/null
@@ -1,48 +0,0 @@
-package FS::part_pkg::billoneday;
-
-use strict;
-use vars qw(@ISA %info);
-use Time::Local qw(timelocal);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'charge a full month every (selectable) billing day',
- 'fields' => {
- 'setup_fee' => { 'name' => 'Setup fee for this package',
- 'default' => 0,
- },
- 'recur_fee' => { 'name' => 'Recurring fee for this package',
- 'default' => 0,
- },
- 'cutoff_day' => { 'name' => 'billing day',
- 'default' => 1,
- },
-
- },
- 'fieldorder' => [ 'setup_fee', 'recur_fee','cutoff_day'],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $mnow = $sdate; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; $sdate = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year); \' + what.recur_fee.value',
- 'freq' => 'm',
- 'weight' => 30,
-);
-
-sub calc_recur {
- my($self, $cust_pkg, $sdate ) = @_;
-
- my $mnow = $$sdate;
- my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
- my $mstart = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year);
- my $mend = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
-
- if($mday > $self->option('cutoff_date') and $mstart != $mnow ) {
- $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
- }
- else{
- $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon, $year);
- }
- $self->option('recur_fee');
-}
-1;
diff --git a/FS/FS/part_pkg/prepaid.pm b/FS/FS/part_pkg/prepaid.pm
deleted file mode 100644
index 407343b..0000000
--- a/FS/FS/part_pkg/prepaid.pm
+++ /dev/null
@@ -1,51 +0,0 @@
-package FS::part_pkg::prepaid;
-
-use strict;
-use vars qw(@ISA %info %recur_action);
-use Tie::IxHash;
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-tie %recur_action, 'Tie::IxHash',
- 'suspend' => 'suspend',
- 'cancel' => 'cancel',
-;
-
-tie my %overlimit_action, 'Tie::IxHash',
- 'overlimit' => 'Default overlimit processing',
- 'cancel' => 'Cancel',
-;
-
-%info = (
- 'name' => 'Prepaid, flat rate',
- #'name' => 'Prepaid (no automatic recurring)', #maybe use it here too
- 'shortname' => 'Prepaid, no automatic cycle',
- 'inherit_fields' => [ 'usage_Mixin', 'global_Mixin' ],
- 'fields' => {
- 'recur_action' => { 'name' => 'Action to take upon reaching end of prepaid preiod',
- 'type' => 'select',
- 'select_options' => \%recur_action,
- },
- 'overlimit_action' => { 'name' => 'Action to take upon reaching a usage limit.',
- 'type' => 'select',
- 'select_options' => \%overlimit_action,
- },
- #XXX if you set overlimit_action to 'cancel', should also have the ability
- # to select a reason
-
- # do we need to disable these?
- map { $_ => { 'disabled' => 1 } } (
- qw(recharge_amount recharge_seconds recharge_upbytes recharge_downbytes
- recharge_totalbytes usage_rollover recharge_reset) ),
- },
- 'fieldorder' => [ qw( recur_action overlimit_action ) ],
- 'weight' => 25,
-);
-
-sub is_prepaid {
- 1;
-}
-
-1;
-
diff --git a/FS/FS/part_pkg/prorate.pm b/FS/FS/part_pkg/prorate.pm
deleted file mode 100644
index 367f152..0000000
--- a/FS/FS/part_pkg/prorate.pm
+++ /dev/null
@@ -1,43 +0,0 @@
-package FS::part_pkg::prorate;
-
-use strict;
-use vars qw(@ISA %info);
-use Time::Local qw(timelocal);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'First partial month pro-rated, then flat-rate (selectable billing day)',
- 'shortname' => 'Prorate (Nth of month billing)',
- 'inherit_fields' => [ 'flat', 'usage_Mixin', 'global_Mixin' ],
- 'fields' => {
- 'recur_temporality' => {'disabled' => 1},
- 'sync_bill_date' => {'disabled' => 1},
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28)',
- 'default' => 1,
- },
-
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
- 'prorate_round_day'=> {
- 'name' => 'When prorating first month, round to '.
- 'the nearest full day',
- 'type' => 'checkbox',
- },
- },
- 'fieldorder' => [ 'cutoff_day', 'add_full_period', 'prorate_round_day' ],
- 'freq' => 'm',
- 'weight' => 20,
-);
-
-sub calc_recur {
- my $self = shift;
- my $cutoff_day = $self->option('cutoff_day') || 1;
- return $self->calc_prorate(@_, $cutoff_day) - $self->calc_discount(@_);
-}
-
-1;
diff --git a/FS/FS/part_pkg/prorate_Mixin.pm b/FS/FS/part_pkg/prorate_Mixin.pm
deleted file mode 100644
index 3f3d86f..0000000
--- a/FS/FS/part_pkg/prorate_Mixin.pm
+++ /dev/null
@@ -1,105 +0,0 @@
-package FS::part_pkg::prorate_Mixin;
-
-use strict;
-use vars qw(@ISA %info);
-use Time::Local qw(timelocal);
-
-@ISA = qw(FS::part_pkg);
-%info = (
- 'disabled' => 1,
-);
-
-=head1 NAME
-
-FS::part_pkg::prorate_Mixin - Mixin class for part_pkg:: classes that
-need to prorate partial months
-
-=head1 SYNOPSIS
-
-package FS::part_pkg::...;
-use base qw( FS::part_pkg::prorate_Mixin );
-
-sub calc_recur {
- ...
- if( conditions that trigger prorate ) {
- # sets $$sdate and $param->{'months'}, returns the prorated charge
- $charges = $self->calc_prorate($cust_pkg, $sdate, $param, $cutoff_day);
- }
- ...
-}
-
-=head METHODS
-
-=item calc_prorate CUST_PKG
-
-Takes all the arguments of calc_recur, followed by a day of the month
-to prorate to (which must be <= 28). Calculates a prorated charge from
-the $sdate to that day, and sets the $sdate and $param->{months} accordingly.
-
-Options:
-- recur_fee: The charge to use for a complete billing period.
-- add_full_period: Bill for the time up to the prorate day plus one full
-billing period after that.
-- prorate_round_day: Round the current time to the nearest full day,
-instead of using the exact time.
-
-=cut
-
-sub calc_prorate {
- my $self = shift;
- my ($cust_pkg, $sdate, $details, $param, $cutoff_day) = @_;
-
- my $charge = $self->option('recur_fee',1) || 0;
- if($cutoff_day) {
- # only works for freq >= 1 month; probably can't be fixed
- my $mnow = $$sdate;
- my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($mnow))[0..5];
- if( $self->option('prorate_round_day',1) ) {
- $mday++ if $hour >= 12;
- $mnow = timelocal(0,0,0,$mday,$mon,$year);
- }
- my $mend;
- my $mstart;
- # if cutoff day > 28, force it to the 1st of next month
- if ( $cutoff_day > 28 ) {
- $cutoff_day = 1;
- # and if we are currently after the 28th, roll the current day
- # forward to that day
- if ( $mday > 28 ) {
- $mday = 1;
- #set $mnow = $mend so the amount billed will be zero
- $mnow = timelocal(0,0,0,1,$mon == 11 ? 0 : $mon + 1,$year+($mon==11));
- }
- }
- if ( $mday >= $cutoff_day ) {
- $mend =
- timelocal(0,0,0,$cutoff_day,$mon == 11 ? 0 : $mon + 1,$year+($mon==11));
- $mstart =
- timelocal(0,0,0,$cutoff_day,$mon,$year);
- }
- else {
- $mend =
- timelocal(0,0,0,$cutoff_day,$mon,$year);
- $mstart =
- timelocal(0,0,0,$cutoff_day,$mon == 0 ? 11 : $mon - 1,$year-($mon==0));
- }
-
- # next bill date will be figured as $$sdate + one period
- $$sdate = $mstart;
-
- my $permonth = $charge / $self->freq;
- my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
-
- if ( $self->option('add_full_period',1) ) {
- # charge a full period in addition to the partial month
- $months += $self->freq;
- $$sdate = $self->add_freq($mstart);
- }
-
- $param->{'months'} = $months;
- $charge = sprintf('%.2f', $permonth * $months);
- }
- return $charge;
-}
-
-1;
diff --git a/FS/FS/part_pkg/prorate_delayed.pm b/FS/FS/part_pkg/prorate_delayed.pm
deleted file mode 100644
index dd1b816..0000000
--- a/FS/FS/part_pkg/prorate_delayed.pm
+++ /dev/null
@@ -1,53 +0,0 @@
-package FS::part_pkg::prorate_delayed;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg;
-
-@ISA = qw(FS::part_pkg::prorate);
-
-%info = (
- 'name' => 'Free (or setup fee) for X days, then prorate, then flat-rate ' .
- '(1st of month billing)',
- 'shortname' => 'Prorate (Nth of month billing), with intro period', #??
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'free_days' => { 'name' => 'Initial free days',
- 'default' => 0,
- },
- 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
- ' commences to notify customer. (0 means'.
- ' no warning)',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ 'free_days', 'recur_notify' ],
- #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
- #'recur' => 'what.recur_fee.value',
- 'weight' => 22,
-);
-
-sub calc_setup {
- my($self, $cust_pkg, $time ) = @_;
-
- my $d = $cust_pkg->bill || $time;
- $d += 86400 * $self->option('free_days');
- $cust_pkg->bill($d);
-
- $self->option('setup_fee');
-}
-
-sub calc_remain {
- my ($self, $cust_pkg, %options) = @_;
- my $last_bill = $cust_pkg->last_bill || 0;
- my $next_bill = $cust_pkg->getfield('bill') || 0;
- my $free_days = $self->option('free_days');
-
- return 0 if $last_bill + (86400 * $free_days) == $next_bill
- && $last_bill == $cust_pkg->setup;
-
- return $self->SUPER::calc_remain($cust_pkg, %options);
-}
-
-1;
diff --git a/FS/FS/part_pkg/recur_Common.pm b/FS/FS/part_pkg/recur_Common.pm
deleted file mode 100644
index 7614d7a..0000000
--- a/FS/FS/part_pkg/recur_Common.pm
+++ /dev/null
@@ -1,70 +0,0 @@
-package FS::part_pkg::recur_Common;
-
-use strict;
-use vars qw( @ISA %info %recur_method );
-use Tie::IxHash;
-use Time::Local;
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = ( 'disabled' => 1 ); #recur_Common not a usable price plan directly
-
-tie %recur_method, 'Tie::IxHash',
- 'anniversary' => 'Charge the recurring fee at the frequency specified above',
- 'prorate' => 'Charge a prorated fee the first time (selectable billing date)',
- 'subscription' => 'Charge the full fee for the first partial period (selectable billing date)',
-;
-
-sub base_recur {
- my $self = shift;
- $self->option('recur_fee', 1) || 0;
-}
-
-sub calc_recur_Common {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_; #only need $sdate & $param
-
- my $charges = 0;
-
- if ( $param->{'increment_next_bill'} ) {
-
- my $recur_method = $self->option('recur_method', 1) || 'anniversary';
-
- $charges = $self->base_recur;
-
- if ( $recur_method eq 'prorate' ) {
- my $cutoff_day = $self->option('cutoff_day') || 1;
- $charges = $self->calc_prorate(@_, $cutoff_day);
- }
- elsif ( $recur_method eq 'anniversary' and
- $self->option('sync_bill_date',1) ) {
- my $next_bill = $cust_pkg->cust_main->next_bill_date;
- if ( defined($next_bill) ) {
- my $cutoff_day = (localtime($next_bill))[3];
- $charges = $self->calc_prorate(@_, $cutoff_day);
- }
- }
- elsif ( $recur_method eq 'subscription' ) {
-
- my $cutoff_day = $self->option('cutoff_day', 1) || 1;
- my ($day, $mon, $year) = ( localtime($$sdate) )[ 3..5 ];
-
- if ( $day < $cutoff_day ) {
- if ( $mon == 0 ) { $mon=11; $year--; }
- else { $mon--; }
- }
-
- $$sdate = timelocal(0, 0, 0, $cutoff_day, $mon, $year);
-
- }#$recur_method eq 'subscription'
-
- $charges -= $self->calc_discount( $cust_pkg, $sdate, $details, $param );
-
- }#increment_next_bill
-
- return $charges;
-
-}
-
-1;
diff --git a/FS/FS/part_pkg/rt_time.pm b/FS/FS/part_pkg/rt_time.pm
deleted file mode 100644
index 03ed1cd..0000000
--- a/FS/FS/part_pkg/rt_time.pm
+++ /dev/null
@@ -1,73 +0,0 @@
-package FS::part_pkg::rt_time;
-
-use strict;
-use FS::Conf;
-use FS::Record qw(qsearchs qsearch);
-use FS::part_pkg::recur_Common;
-use Carp qw(cluck);
-
-our @ISA = qw(FS::part_pkg::recur_Common);
-
-our $DEBUG = 0;
-
-our %info = (
- 'name' => 'Bill from Time Worked on tickets in RT',
- 'shortname' => 'Project Billing (RT)',
- 'weight' => 55,
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'base_rate' => { 'name' => 'Rate (per minute)',
- 'default' => 0,
- },
- 'recur_fee' => {'disabled' => 1},
- },
- 'fieldorder' => [ 'base_rate' ],
-);
-
-sub calc_setup {
- my($self, $cust_pkg ) = @_;
- $self->option('setup_fee');
-}
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- my $charges = 0;
-
- $charges += $self->calc_usage(@_);
- $charges += $self->calc_recur_Common(@_);
-
- $charges;
-
-}
-
-sub can_discount { 0; }
-
-sub calc_cancel {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- $self->calc_usage(@_);
-}
-
-sub calc_usage {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- my $last_bill = $cust_pkg->get('last_bill') || $cust_pkg->get('setup');
- my @tickets = @{ FS::TicketSystem->comments_on_tickets( $cust_pkg->custnum, 100, $last_bill ) };
-
- my $charges = 0;
-
- my $rate = $self->option('base_rate');
-
- foreach my $ding ( @tickets) {
- $charges += sprintf('%.2f', $ding->{'timetaken'} * $rate);
- push @$details, join( ", ", ("($ding->{timetaken}) Minutes", substr($ding->{'content'},0,255)));
- }
- cluck $rate, $charges, @$details if $DEBUG > 0;
- return $charges;
-}
-
-1;
diff --git a/FS/FS/part_pkg/sesmon_hour.pm b/FS/FS/part_pkg/sesmon_hour.pm
deleted file mode 100644
index 97274d0..0000000
--- a/FS/FS/part_pkg/sesmon_hour.pm
+++ /dev/null
@@ -1,50 +0,0 @@
-package FS::part_pkg::sesmon_hour;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Base charge plus charge per-hour from the session monitor',
- 'shortname' => 'Session monitor (per-hour)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'recur_included_hours' => { 'name' => 'Hours included',
- 'default' => 0,
- },
- 'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ 'recur_included_hours', 'recur_hourly_charge' ],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $hours = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; \' + what.recur_fee.value + \' + \' + what.recur_hourly_charge.value + \' * $hours;\'',
- 'weight' => 80,
-);
-
-sub calc_recur {
- my($self, $cust_pkg ) = @_;
-
- my $hours = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 3600;
- $hours -= $self->option('recur_included_hours');
- $hours = 0 if $hours < 0;
-
- $self->option('recur_fee') + $hours * $self->option('recur_hourly_charge');
-
-}
-
-sub can_discount { 0; }
-
-sub is_free_options {
- qw( setup_fee recur_fee recur_hourly_charge );
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee');
-}
-
-1;
diff --git a/FS/FS/part_pkg/sesmon_minute.pm b/FS/FS/part_pkg/sesmon_minute.pm
deleted file mode 100644
index 9c8dfd1..0000000
--- a/FS/FS/part_pkg/sesmon_minute.pm
+++ /dev/null
@@ -1,49 +0,0 @@
-package FS::part_pkg::sesmon_minute;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Base charge plus charge per-minute from the session monitor',
- 'shortname' => 'Session monitor (per-minute)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'recur_included_min' => { 'name' => 'Minutes included',
- 'default' => 0,
- },
- 'recur_minly_charge' => { 'name' => 'Additional charge per minute',
- 'default' => 0,
- },
- },
- 'fieldorder' => [ 'recur_included_min', 'recur_minly_charge' ],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $min = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 60 - \' + what.recur_included_min.value + \'; $min = 0 if $min < 0; \' + what.recur_fee.value + \' + \' + what.recur_minly_charge.value + \' * $min;\'',
- 'weight' => 80,
-);
-
-
-sub calc_recur {
- my( $self, $cust_pkg ) = @);
- my $min = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 60;
- $min -= $self->option('recur_included_min');
- $min = 0 if $min < 0;
-
- $self->option('recur_fee') + $min * $self->option('recur_minly_charge');
-}
-
-sub can_discount { 0; }
-
-sub is_free_options {
- qw( setup_fee recur_fee recur_minly_charge );
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee');
-}
-
-1;
diff --git a/FS/FS/part_pkg/sql_external.pm b/FS/FS/part_pkg/sql_external.pm
deleted file mode 100644
index 8d43086..0000000
--- a/FS/FS/part_pkg/sql_external.pm
+++ /dev/null
@@ -1,77 +0,0 @@
-package FS::part_pkg::sql_external;
-
-use strict;
-use base qw( FS::part_pkg::recur_Common );
-use vars qw( %info );
-use DBI;
-#use FS::Record qw(qsearch qsearchs);
-
-%info = (
- 'name' => 'Base charge plus additional fees for external services from a configurable SQL query',
- 'shortname' => 'External SQL query',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28) for prorating or '.
- 'subscription',
- 'default' => '1',
- },
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
-
- 'recur_method' => { 'name' => 'Recurring fee method',
- #'type' => 'radio',
- #'options' => \%recur_method,
- 'type' => 'select',
- 'select_options' => \%FS::part_pkg::recur_Common::recur_method,
- },
- 'datasrc' => { 'name' => 'DBI data source',
- 'default' => '',
- },
- 'db_username' => { 'name' => 'Database username',
- 'default' => '',
- },
- 'db_password' => { 'name' => 'Database password',
- 'default' => '',
- },
- 'query' => { 'name' => 'SQL query',
- 'default' => '',
- },
- },
- 'fieldorder' => [qw( recur_method cutoff_day
- add_full_period datasrc db_username db_password query
- )],
- 'weight' => '58',
-);
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg) = @_; #, $sdate, $details, $param ) = @_;
-
- my $price = $self->calc_recur_Common(@_);
-
- my $dbh = DBI->connect( map { $self->option($_) }
- qw( datasrc db_username db_password )
- )
- or die $DBI::errstr;
-
- my $sth = $dbh->prepare( $self->option('query') )
- or die $dbh->errstr;
-
- foreach my $cust_svc (
- grep { $_->part_svc->svcdb eq "svc_external" } $cust_pkg->cust_svc
- ) {
- my $id = $cust_svc->svc_x->id;
- $sth->execute($id) or die $sth->errstr;
- $price += $sth->fetchrow_arrayref->[0];
- }
-
- $price;
-}
-
-sub can_discount { 0; }
-
-sub is_free { 0; }
-
-1;
diff --git a/FS/FS/part_pkg/sql_generic.pm b/FS/FS/part_pkg/sql_generic.pm
deleted file mode 100644
index cf38257..0000000
--- a/FS/FS/part_pkg/sql_generic.pm
+++ /dev/null
@@ -1,81 +0,0 @@
-package FS::part_pkg::sql_generic;
-
-use strict;
-use vars qw(@ISA %info);
-use DBI;
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Base charge plus a per-domain metered rate from a configurable SQL query',
- 'shortname' => 'Bulk (per-domain from SQL query)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'recur_included' => { 'name' => 'Units included',
- 'default' => 0,
- },
- 'recur_unit_charge' => { 'name' => 'Additional charge per unit',
- 'default' => 0,
- },
- 'datasrc' => { 'name' => 'DBI data source',
- 'default' => '',
- },
- 'db_username' => { 'name' => 'Database username',
- 'default' => '',
- },
- 'db_password' => { 'name' => 'Database username',
- 'default' => '',
- },
- 'query' => { 'name' => 'SQL query',
- 'default' => '',
- },
- },
- 'fieldorder' => [qw( recur_included recur_unit_charge datasrc db_username db_password query )],
- # 'setup' => 'what.setup_fee.value',
- # 'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\") or die $DBI::errstr; \'',
- #'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\", \"\' + what.db_password.value + \'\" ) or die $DBI::errstr; my $sth = $dbh->prepare(\"\' + what.query.value + \'\") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq \"svc_domain\" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_fee.value + \' + $units * \' + what.recur_unit_charge.value + \';\'',
- #'recur' => '\'my $dbh = DBI->connect("\' + what.datasrc.value + \'", "\' + what.db_username.value + \'", "\' what.db_password.value + \'" ) or die $DBI::errstr; my $sth = $dbh->prepare("\' + what.query.value + \'") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq "svc_domain" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_fee.value + \' + $units * \' + what.recur_unit_charge + \';\'',
- 'weight' => '56',
-);
-
-sub calc_recur {
- my($self, $cust_pkg ) = @_;
-
- my $dbh = DBI->connect( map { $self->option($_) }
- qw( datasrc db_username db_password )
- )
- or die $DBI::errstr;
-
- my $sth = $dbh->prepare( $self->option('query') )
- or die $dbh->errstr;
-
- my $units = 0;
- foreach my $cust_svc (
- grep { $_->part_svc->svcdb eq "svc_domain" } $cust_pkg->cust_svc
- ) {
- my $domain = $cust_svc->svc_x->domain;
- $sth->execute($domain) or die $sth->errstr;
-
- $units += $sth->fetchrow_arrayref->[0];
- }
-
- $units -= $self->option('recur_included');
- $units = 0 if $units < 0;
-
- $self->option('recur_fee') + $units * $self->option('recur_unit_charge');
-}
-
-sub can_discount { 0; }
-
-sub is_free_options {
- qw( setup_fee recur_fee recur_unit_charge );
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee');
-}
-
-1;
diff --git a/FS/FS/part_pkg/sqlradacct_hour.pm b/FS/FS/part_pkg/sqlradacct_hour.pm
deleted file mode 100644
index 3cc46ac..0000000
--- a/FS/FS/part_pkg/sqlradacct_hour.pm
+++ /dev/null
@@ -1,163 +0,0 @@
-package FS::part_pkg::sqlradacct_hour;
-
-use strict;
-use vars qw(@ISA %info);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'Base charge plus per-hour (and for data) from an SQL RADIUS radacct table',
- 'shortname' => 'Usage charges from RADIUS',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'recur_included_hours' => { 'name' => 'Hours included',
- 'default' => 0,
- },
- 'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
- 'default' => 0,
- },
- 'recur_hourly_cap' => { 'name' => 'Maximum overage charge for hours'.
- ' (0 means no cap)',
-
- 'default' => 0,
- },
-
- 'recur_included_input' => { 'name' => 'Upload megabytes included',
- 'default' => 0,
- },
- 'recur_input_charge' => { 'name' =>
- 'Additional charge per megabyte upload',
- 'default' => 0,
- },
- 'recur_input_cap' => { 'name' => 'Maximum overage charge for upload'.
- ' (0 means no cap)',
- 'default' => 0,
- },
-
- 'recur_included_output' => { 'name' => 'Download megabytes included',
- 'default' => 0,
- },
- 'recur_output_charge' => { 'name' =>
- 'Additional charge per megabyte download',
- 'default' => 0,
- },
- 'recur_output_cap' => { 'name' => 'Maximum overage charge for download'.
- ' (0 means no cap)',
- 'default' => 0,
- },
-
- 'recur_included_total' => { 'name' =>
- 'Total megabytes included',
- 'default' => 0,
- },
- 'recur_total_charge' => { 'name' =>
- 'Additional charge per megabyte total',
- 'default' => 0,
- },
- 'recur_total_cap' => { 'name' => 'Maximum overage charge for total'.
- ' megabytes (0 means no cap)',
- 'default' => 0,
- },
-
- 'global_cap' => { 'name' => 'Global cap on all overage charges'.
- ' (0 means no cap)',
- 'default' => 0,
- },
-
- },
- 'fieldorder' => [qw( recur_included_hours recur_hourly_charge recur_hourly_cap recur_included_input recur_input_charge recur_input_cap recur_included_output recur_output_charge recur_output_cap recur_included_total recur_total_charge recur_total_cap global_cap )],
- #'setup' => 'what.setup_fee.value',
- #'recur' => '\'my $last_bill = $cust_pkg->last_bill; my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $sdate ) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; my $input = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctInputOctets\" ) / 1048576; my $output = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctOutputOctets\" ) / 1048576; my $total = $input + $output - \' + what.recur_included_total.value + \'; $total = 0 if $total < 0; my $input = $input - \' + what.recur_included_input.value + \'; $input = 0 if $input < 0; my $output = $output - \' + what.recur_included_output.value + \'; $output = 0 if $output < 0; my $totalcharge = sprintf(\"%.2f\", \' + what.recur_total_charge.value + \' * $total); my $inputcharge = sprintf(\"%.2f\", \' + what.recur_input_charge.value + \' * $input); my $outputcharge = sprintf(\"%.2f\", \' + what.recur_output_charge.value + \' * $output); my $hourscharge = sprintf(\"%.2f\", \' + what.recur_hourly_charge.value + \' * $hours); if ( \' + what.recur_total_charge.value + \' > 0 ) { push @details, \"Last month\\\'s data \". sprintf(\"%.1f\", $total). \" megs: \\\$$totalcharge\" } if ( \' + what.recur_input_charge.value + \' > 0 ) { push @details, \"Last month\\\'s download \". sprintf(\"%.1f\", $input). \" megs: \\\$$inputcharge\" } if ( \' + what.recur_output_charge.value + \' > 0 ) { push @details, \"Last month\\\'s upload \". sprintf(\"%.1f\", $output). \" megs: \\\$$outputcharge\" } if ( \' + what.recur_hourly_charge.value + \' > 0 ) { push @details, \"Last month\\\'s time \". sprintf(\"%.1f\", $hours). \" hours: \\\$$hourscharge\"; } \' + what.recur_fee.value + \' + $hourscharge + $inputcharge + $outputcharge + $totalcharge ;\'',
- 'weight' => 40,
-);
-
-sub calc_recur {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- my $last_bill = $cust_pkg->last_bill;
- my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $$sdate ) / 3600;
- $hours -= $self->option('recur_included_hours');
- $hours = 0 if $hours < 0;
-
- my $input = $cust_pkg->attribute_since_sqlradacct( $last_bill,
- $$sdate,
- 'AcctInputOctets' )
- / 1048576;
-
- my $output = $cust_pkg->attribute_since_sqlradacct( $last_bill,
- $$sdate,
- 'AcctOutputOctets' )
- / 1048576;
-
- my $total = $input + $output - $self->option('recur_included_total');
- $total = 0 if $total < 0;
- $input = $input - $self->option('recur_included_input');
- $input = 0 if $input < 0;
- $output = $output - $self->option('recur_included_output');
- $output = 0 if $output < 0;
-
- my $totalcharge =
- $total * sprintf('%.2f', $self->option('recur_total_charge'));
- $totalcharge = $self->option('recur_total_cap')
- if $self->option('recur_total_cap')
- && $totalcharge > $self->option('recur_total_cap');
-
- my $inputcharge =
- $input * sprintf('%.2f', $self->option('recur_input_charge'));
- $inputcharge = $self->option('recur_input_cap')
- if $self->option('recur_input_cap')
- && $inputcharge > $self->option('recur_input_cap');
-
- my $outputcharge =
- $output * sprintf('%.2f', $self->option('recur_output_charge'));
- $outputcharge = $self->option('recur_output_cap')
- if $self->option('recur_output_cap')
- && $outputcharge > $self->option('recur_output_cap');
-
- my $hourscharge =
- $hours * sprintf('%.2f', $self->option('recur_hourly_charge'));
- $hourscharge = $self->option('recur_hourly_cap')
- if $self->option('recur_hourly_cap')
- && $hourscharge > $self->option('recur_hourly_cap');
-
- if ( $self->option('recur_total_charge') > 0 ) {
- push @$details, "Last month's data ".
- sprintf('%.1f', $total). " megs: $totalcharge";
- }
- if ( $self->option('recur_input_charge') > 0 ) {
- push @$details, "Last month's download ".
- sprintf('%.1f', $input). " megs: $inputcharge";
- }
- if ( $self->option('recur_output_charge') > 0 ) {
- push @$details, "Last month's upload ".
- sprintf('%.1f', $output). " megs: $outputcharge";
- }
- if ( $self->option('recur_hourly_charge') > 0 ) {
- push @$details, "Last month\'s time ".
- sprintf('%.1f', $hours). " hours: $hourscharge";
- }
-
- my $charges = $hourscharge + $inputcharge + $outputcharge + $totalcharge;
- if ( $self->option('global_cap') && $charges > $self->option('global_cap') ) {
- $charges = $self->option('global_cap');
- push @$details, "Usage charges capped at: $charges";
- }
-
- $self->option('recur_fee') + $charges;
-}
-
-sub can_discount { 0; }
-
-sub is_free_options {
- qw( setup_fee recur_fee recur_hourly_charge
- recur_input_charge recur_output_charge recur_total_charge );
-}
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee');
-}
-
-1;
diff --git a/FS/FS/part_pkg/subscription.pm b/FS/FS/part_pkg/subscription.pm
deleted file mode 100644
index 3c5f96b..0000000
--- a/FS/FS/part_pkg/subscription.pm
+++ /dev/null
@@ -1,108 +0,0 @@
-package FS::part_pkg::subscription;
-
-use strict;
-use vars qw(@ISA %info);
-use Time::Local qw(timelocal);
-#use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg::flat;
-
-@ISA = qw(FS::part_pkg::flat);
-
-%info = (
- 'name' => 'First partial month full charge, then flat-rate (selectable billing day)',
- 'shortname' => 'Subscription (Nth of month, full charge for first)',
- 'inherit_fields' => [ 'usage_Mixin', 'global_Mixin' ],
- 'fields' => {
- 'cutoff_day' => { 'name' => 'Billing day',
- 'default' => 1,
- },
- 'seconds' => { 'name' => 'Time limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- },
- 'upbytes' => { 'name' => 'Upload limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'downbytes' => { 'name' => 'Download limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'totalbytes' => { 'name' => 'Transfer limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_amount' => { 'name' => 'Cost of recharge for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*(\.\d{2})?$/ },
- },
- 'recharge_seconds' => { 'name' => 'Recharge time for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- },
- 'recharge_upbytes' => { 'name' => 'Recharge upload for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_downbytes' => { 'name' => 'Recharge download for this package', 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_totalbytes' => { 'name' => 'Recharge transfer for this package', 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'usage_rollover' => { 'name' => 'Allow usage from previous period to roll '.
- 'over into current period',
- 'type' => 'checkbox',
- },
- 'recharge_reset' => { 'name' => 'Reset usage to these values on manual '.
- 'package recharge',
- 'type' => 'checkbox',
- },
-
- #it would be better if this had to be turned on, its confusing
- 'externalid' => { 'name' => 'Optional External ID',
- 'default' => '',
- },
- },
- 'fieldorder' => [ 'cutoff_day', 'seconds',
- 'upbytes', 'downbytes', 'totalbytes',
- 'recharge_amount', 'recharge_seconds', 'recharge_upbytes',
- 'recharge_downbytes', 'recharge_totalbytes',
- 'usage_rollover', 'recharge_reset', 'externalid' ],
- 'freq' => 'm',
- 'weight' => 30,
-);
-
-sub calc_recur {
- my($self, $cust_pkg, $sdate, $details, $param ) = @_;
- my $cutoff_day = $self->option('cutoff_day', 1) || 1;
- my $mnow = $$sdate;
- my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
-
- if ( $mday < $cutoff_day ) {
- if ($mon==0) {$mon=11;$year--;}
- else {$mon--;}
- }
-
- $$sdate = timelocal(0,0,0,$cutoff_day,$mon,$year);
-
- my $br = $self->base_recur($cust_pkg);
-
- my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
-
- sprintf('%.2f', $br - $discount);
-}
-
-1;
diff --git a/FS/FS/part_pkg/usage_Mixin.pm b/FS/FS/part_pkg/usage_Mixin.pm
deleted file mode 100644
index 028fce7..0000000
--- a/FS/FS/part_pkg/usage_Mixin.pm
+++ /dev/null
@@ -1,77 +0,0 @@
-package FS::part_pkg::usage_Mixin;
-
-use strict;
-use vars qw( @ISA %info );
-use FS::part_pkg;
-use FS::UI::bytecount;
-@ISA = qw(FS::part_pkg);
-
-# Field definitions for time and data usage, other than CDRs.
-
-%info = (
- 'disabled' => 1,
- 'fields' => {
- 'seconds' => { 'name' => 'Time limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- },
- 'upbytes' => { 'name' => 'Upload limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'downbytes' => { 'name' => 'Download limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'totalbytes' => { 'name' => 'Transfer limit for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_amount' => { 'name' => 'Cost of recharge for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*(\.\d{2})?$/ },
- },
- 'recharge_seconds' => { 'name' => 'Recharge time for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- },
- 'recharge_upbytes' => { 'name' => 'Recharge upload for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_downbytes' => { 'name' => 'Recharge download for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'recharge_totalbytes' => { 'name' => 'Recharge transfer for this package',
- 'default' => '',
- 'check' => sub { shift =~ /^\d*$/ },
- 'format' => \&FS::UI::bytecount::display_bytecount,
- 'parse' => \&FS::UI::bytecount::parse_bytecount,
- },
- 'usage_rollover' => { 'name' => 'Allow usage from previous period to roll '.
- ' over into current period',
- 'type' => 'checkbox',
- },
- 'recharge_reset' => { 'name' => 'Reset usage to these values on manual '.
- 'package recharge',
- 'type' => 'checkbox',
- },
- },
- 'fieldorder' => [ qw( seconds upbytes downbytes totalbytes
- recharge_amount recharge_seconds recharge_upbytes
- recharge_downbytes recharge_totalbytes
- usage_rollover recharge_reset ) ],
-);
-
-1;
diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm
deleted file mode 100644
index 5dbd115..0000000
--- a/FS/FS/part_pkg/voip_cdr.pm
+++ /dev/null
@@ -1,925 +0,0 @@
-package FS::part_pkg::voip_cdr;
-
-use strict;
-use vars qw(@ISA $DEBUG %info);
-use Date::Format;
-use Tie::IxHash;
-use FS::Conf;
-use FS::Record qw(qsearchs qsearch);
-use FS::part_pkg::recur_Common;
-use FS::cdr;
-use FS::rate;
-use FS::rate_prefix;
-use FS::rate_detail;
-use FS::part_pkg::recur_Common;
-
-use List::Util qw(first min);
-
-@ISA = qw(FS::part_pkg::recur_Common);
-
-$DEBUG = 0;
-
-tie my %cdr_svc_method, 'Tie::IxHash',
- 'svc_phone.phonenum' => 'Phone numbers (svc_phone.phonenum)',
- 'svc_pbx.title' => 'PBX name (svc_pbx.title)',
- 'svc_pbx.svcnum' => 'Freeside service # (svc_pbx.svcnum)',
-;
-
-tie my %rating_method, 'Tie::IxHash',
- 'prefix' => 'Rate calls by using destination prefix to look up a region and rate according to the internal prefix and rate tables',
-# 'upstream' => 'Rate calls based on upstream data: If the call type is "1", map the upstream rate ID directly to an internal rate (rate_detail), otherwise, pass the upstream price through directly.',
- 'upstream_simple' => 'Simply pass through and charge the "upstream_price" amount.',
- 'single_price' => 'A single price per minute for all calls.',
-;
-
-#tie my %cdr_location, 'Tie::IxHash',
-# 'internal' => 'Internal: CDR records imported into the internal CDR table',
-# 'external' => 'External: CDR records queried directly from an external '.
-# 'Asterisk (or other?) CDR table',
-#;
-
-tie my %temporalities, 'Tie::IxHash',
- 'upcoming' => "Upcoming (future)",
- 'preceding' => "Preceding (past)",
-;
-
-tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
-
-%info = (
- 'name' => 'VoIP rating by plan of CDR records in an internal (or external) SQL table',
- 'shortname' => 'VoIP/telco CDR rating (standard)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- #false laziness w/flat.pm
- 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
- 'type' => 'select',
- 'select_options' => \%temporalities,
- },
-
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28) for prorating or '.
- 'subscription',
- 'default' => '1',
- },
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
- 'recur_method' => { 'name' => 'Recurring fee method',
- #'type' => 'radio',
- #'options' => \%recur_method,
- 'type' => 'select',
- 'select_options' => \%FS::part_pkg::recur_Common::recur_method,
- },
-
- 'cdr_svc_method' => { 'name' => 'CDR service matching method',
- 'type' => 'radio',
- 'options' => \%cdr_svc_method,
- },
-
- 'rating_method' => { 'name' => 'Rating method',
- 'type' => 'radio',
- 'options' => \%rating_method,
- },
-
- 'ratenum' => { 'name' => 'Rate plan',
- 'type' => 'select',
- 'select_table' => 'rate',
- 'select_key' => 'ratenum',
- 'select_label' => 'ratename',
- },
-
- 'min_included' => { 'name' => 'Minutes included when using "single price per minute" rating method',
- },
-
-
- 'min_charge' => { 'name' => 'Charge per minute when using "single price per minute" rating method',
- },
-
- 'sec_granularity' => { 'name' => 'Granularity when using "single price per minute" rating method',
- 'type' => 'select',
- 'select_options' => \%granularity,
- },
-
- 'ignore_unrateable' => { 'name' => 'Ignore calls without a rate in the rate tables. By default, the system will throw a fatal error upon encountering unrateable calls.',
- 'type' => 'checkbox',
- },
-
- 'default_prefix' => { 'name' => 'Default prefix optionally prepended to customer DID numbers when searching for CDR records',
- 'default' => '+1',
- },
-
- 'disable_src' => { 'name' => 'Disable rating of CDR records based on the "src" field in addition to "charged_party"',
- 'type' => 'checkbox'
- },
-
- 'domestic_prefix' => { 'name' => 'Destination prefix for domestic CDR records',
- 'default' => '1',
- },
-
-# 'domestic_prefix_required' => { 'name' => 'Require explicit destination prefix for domestic CDR records',
-# 'type' => 'checkbox',
-# },
-
- 'international_prefix' => { 'name' => 'Destination prefix for international CDR records',
- 'default' => '011',
- },
-
- 'disable_tollfree' => { 'name' => 'Disable automatic toll-free processing',
- 'type' => 'checkbox',
- },
-
- 'use_amaflags' => { 'name' => 'Do not charge for CDRs where the amaflags field is not set to "2" ("BILL"/"BILLING").',
- 'type' => 'checkbox',
- },
-
- 'use_disposition' => { 'name' => 'Do not charge for CDRs where the disposition flag is not set to "ANSWERED".',
- 'type' => 'checkbox',
- },
-
- 'use_disposition_taqua' => { 'name' => 'Do not charge for CDRs where the disposition is not set to "100" (Taqua).',
- 'type' => 'checkbox',
- },
-
- 'use_carrierid' => { 'name' => 'Do not charge for CDRs where the Carrier ID is not set to: ',
- },
-
- 'use_cdrtypenum' => { 'name' => 'Do not charge for CDRs where the CDR Type is not set to: ',
- },
-
- 'skip_dst_prefix' => { 'name' => 'Do not charge for CDRs where the destination number starts with any of these values: ',
- },
-
- 'skip_dcontext' => { 'name' => 'Do not charge for CDRs where the dcontext is set to any of these (comma-separated) values: ',
- },
-
- 'skip_dstchannel_prefix' => { 'name' => 'Do not charge for CDRs where the dstchannel starts with:',
- },
-
- 'skip_src_length_more' => { 'name' => 'Do not charge for CDRs where the source is more than this many digits:',
- },
-
- 'noskip_src_length_accountcode_tollfree' => { 'name' => 'Do charge for CDRs where source is equal or greater than the specified digits, when accountcode is toll free',
- 'type' => 'checkbox',
- },
-
- 'accountcode_tollfree_ratenum' => {
- 'name' => 'Optional alternate rate plan when accountcode is toll free: ',
- 'type' => 'select',
- 'select_table' => 'rate',
- 'select_key' => 'ratenum',
- 'select_label' => 'ratename',
- 'disable_empty' => 0,
- 'empty_label' => '',
- },
-
- 'skip_dst_length_less' => { 'name' => 'Do not charge for CDRs where the destination is less than this many digits:',
- },
-
- 'noskip_dst_length_accountcode_tollfree' => { 'name' => 'Do charge for CDRs where dst is less than the specified digits, when accountcode is toll free',
- 'type' => 'checkbox',
- },
-
- 'skip_lastapp' => { 'name' => 'Do not charge for CDRs where the lastapp matches this value: ',
- },
-
- 'skip_max_callers' => { 'name' => 'Do not charge for CDRs where max_callers is less than or equal to this value: ',
- },
-
- 'use_duration' => { 'name' => 'Calculate usage based on the duration field instead of the billsec field',
- 'type' => 'checkbox',
- },
-
- '411_rewrite' => { 'name' => 'Rewrite these (comma-separated) destination numbers to 411 for rating purposes (also ignore any carrierid check): ',
- },
-
- #false laziness w/cdr_termination.pm
- 'output_format' => { 'name' => 'CDR invoice display format',
- 'type' => 'select',
- 'select_options' => { FS::cdr::invoice_formats() },
- 'default' => 'default', #XXX test
- },
-
- 'usage_section' => { 'name' => 'Section in which to place usage charges (whether separated or not): ',
- },
-
- 'summarize_usage' => { 'name' => 'Include usage summary with recurring charges when usage is in separate section',
- 'type' => 'checkbox',
- },
-
- 'usage_mandate' => { 'name' => 'Always put usage details in separate section',
- 'type' => 'checkbox',
- },
- #eofalse
-
- 'bill_every_call' => { 'name' => 'Generate an invoice immediately for every call (as well any setup fee, upon first payment). Useful for prepaid.',
- 'type' => 'checkbox',
- },
-
- 'bill_inactive_svcs' => { 'name' => 'Bill for all phone numbers that were active during the billing period',
- 'type' => 'checkbox',
- },
-
- 'count_available_phones' => { 'name' => 'Consider for tax purposes the number of lines to be svc_phones that may be provisioned rather than those that actually are.',
- 'type' => 'checkbox',
- },
-
- #XXX also have option for an external db
-# 'cdr_location' => { 'name' => 'CDR database location'
-# 'type' => 'select',
-# 'select_options' => \%cdr_location,
-# 'select_callback' => {
-# 'external' => {
-# 'enable' => [ 'datasrc', 'username', 'password' ],
-# },
-# 'internal' => {
-# 'disable' => [ 'datasrc', 'username', 'password' ],
-# }
-# },
-# },
-# 'datasrc' => { 'name' => 'DBI data source for external CDR table',
-# 'disabled' => 'Y',
-# },
-# 'username' => { 'name' => 'External database username',
-# 'disabled' => 'Y',
-# },
-# 'password' => { 'name' => 'External database password',
-# 'disabled' => 'Y',
-# },
-
- },
- 'fieldorder' => [qw(
- recur_temporality
- recur_method cutoff_day
- add_full_period
- cdr_svc_method
- rating_method ratenum min_charge sec_granularity
- ignore_unrateable
- default_prefix
- disable_src
- domestic_prefix international_prefix
- disable_tollfree
- use_amaflags use_disposition
- use_disposition_taqua use_carrierid use_cdrtypenum
- skip_dcontext skip_dst_prefix
- skip_dstchannel_prefix skip_src_length_more
- noskip_src_length_accountcode_tollfree
- accountcode_tollfree_ratenum
- skip_dst_length_less
- noskip_dst_length_accountcode_tollfree
- skip_lastapp
- skip_max_callers
- use_duration
- 411_rewrite
- output_format usage_mandate summarize_usage usage_section
- bill_every_call bill_inactive_svcs
- count_available_phones
- )
- ],
- 'weight' => 40,
-);
-
-sub calc_setup {
- my($self, $cust_pkg ) = @_;
- $self->option('setup_fee');
-}
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- my $charges = 0;
-
- $charges += $self->calc_usage(@_);
- $charges += $self->calc_recur_Common(@_);
-
- $charges;
-
-}
-
-sub calc_cancel {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- $self->calc_usage(@_);
-}
-
-#false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again
-
-sub calc_usage {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- #my $last_bill = $cust_pkg->last_bill;
- my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
-
- return 0
- if $self->option('recur_temporality', 1) eq 'preceding'
- && ( $last_bill eq '' || $last_bill == 0 );
-
- my $ratenum = $cust_pkg->part_pkg->option('ratenum');
-
- my $spool_cdr = $cust_pkg->cust_main->spool_cdr;
-
- my %included_min = ();
-
- my $charges = 0;
-
-# my $downstream_cdr = '';
-
- my $cdr_svc_method = $self->option('cdr_svc_method',1)||'svc_phone.phonenum';
- my $rating_method = $self->option('rating_method') || 'prefix';
- my $intl = $self->option('international_prefix') || '011';
- my $domestic_prefix = $self->option('domestic_prefix');
- my $disable_tollfree = $self->option('disable_tollfree');
- my $ignore_unrateable = $self->option('ignore_unrateable', 'Hush!');
- my $use_duration = $self->option('use_duration');
-
- my $output_format = $self->option('output_format', 'Hush!')
- || ( $rating_method eq 'upstream_simple'
- ? 'simple'
- : 'default'
- );
-
- my @dirass = ();
- if ( $self->option('411_rewrite') ) {
- my $dirass = $self->option('411_rewrite');
- $dirass =~ s/\s//g;
- @dirass = split(',', $dirass);
- }
-
- my %interval_cache = (); # for timed rates
-
- #for check_chargable, so we don't keep looking up options inside the loop
- my %opt_cache = ();
-
- eval "use Text::CSV_XS;";
- die $@ if $@;
- my $csv = new Text::CSV_XS;
-
- my($svc_table, $svc_field) = split('\.', $cdr_svc_method);
-
- my @cust_svc;
- if( $self->option('bill_inactive_svcs',1) ) {
- #XXX in this mode do we need to restrict the set of CDRs by date also?
- @cust_svc = $cust_pkg->h_cust_svc($$sdate, $last_bill);
- }
- else {
- @cust_svc = $cust_pkg->cust_svc;
- }
- @cust_svc = grep { $_->part_svc->svcdb eq $svc_table } @cust_svc;
-
- foreach my $cust_svc (@cust_svc) {
-
- my $svc_x;
- if( $self->option('bill_inactive_svcs',1) ) {
- $svc_x = $cust_svc->h_svc_x($$sdate, $last_bill);
- }
- else {
- $svc_x = $cust_svc->svc_x;
- }
- my %options = (
- 'disable_src' => $self->option('disable_src'),
- 'default_prefix' => $self->option('default_prefix'),
- 'status' => '',
- 'for_update' => 1,
- ); # $last_bill, $$sdate )
- $options{'by_svcnum'} = 1 if $svc_field eq 'svcnum';
-
- foreach my $cdr (
- $svc_x->get_cdrs( %options )
- ) {
- if ( $DEBUG > 1 ) {
- warn "rating CDR $cdr\n".
- join('', map { " $_ => ". $cdr->{$_}. "\n" } keys %$cdr );
- }
-
- my $rate_detail;
- my( $rate_region, $regionnum );
- my $rate;
- my $pretty_destnum;
- my $charge = '';
- my $seconds = '';
- my $weektime = '';
- my $regionname = '';
- my $classnum = '';
- my $countrycode;
- my $number;
-
- my @call_details = ();
- if ( $rating_method eq 'prefix' ) {
-
- my $da_rewrote = 0;
- if ( length($cdr->dst) && grep { $cdr->dst eq $_ } @dirass ){
- $cdr->dst('411');
- $da_rewrote = 1;
- }
-
- my $reason = $self->check_chargable( $cdr,
- 'da_rewrote' => $da_rewrote,
- 'option_cache' => \%opt_cache,
- );
-
- if ( $reason ) {
-
- warn "not charging for CDR ($reason)\n" if $DEBUG;
- $charge = 0;
-
- } else {
-
- ###
- # look up rate details based on called station id
- # (or calling station id for toll free calls)
- ###
-
- my( $to_or_from );
- if ( $cdr->is_tollfree && ! $disable_tollfree )
- { #tollfree call
- $to_or_from = 'from';
- $number = $cdr->src;
- } else { #regular call
- $to_or_from = 'to';
- $number = $cdr->dst;
- }
-
- warn "parsing call $to_or_from $number\n" if $DEBUG;
-
- #remove non-phone# stuff and whitespace
- $number =~ s/\s//g;
-# my $proto = '';
-# $dest =~ s/^(\w+):// and $proto = $1; #sip:
-# my $siphost = '';
-# $dest =~ s/\@(.*)$// and $siphost = $1; # @10.54.32.1, @sip.example.com
-
- #determine the country code
- $countrycode = '';
- if ( $number =~ /^$intl(((\d)(\d))(\d))(\d+)$/
- || $number =~ /^\+(((\d)(\d))(\d))(\d+)$/
- )
- {
-
- my( $three, $two, $one, $u1, $u2, $rest ) = ( $1,$2,$3,$4,$5,$6 );
- #first look for 1 digit country code
- if ( qsearch('rate_prefix', { 'countrycode' => $one } ) ) {
- $countrycode = $one;
- $number = $u1.$u2.$rest;
- } elsif ( qsearch('rate_prefix', { 'countrycode' => $two } ) ) { #or 2
- $countrycode = $two;
- $number = $u2.$rest;
- } else { #3 digit country code
- $countrycode = $three;
- $number = $rest;
- }
-
- } else {
- $countrycode = $domestic_prefix || '1';
- $number =~ s/^$countrycode//;# if length($number) > 10;
- }
-
- warn "rating call $to_or_from +$countrycode $number\n" if $DEBUG;
- $pretty_destnum = "+$countrycode $number";
- #asterisks here causes inserting the detail to barf, so:
- $pretty_destnum =~ s/\*//g;
-
- my $eff_ratenum = $cdr->is_tollfree('accountcode')
- ? $cust_pkg->part_pkg->option('accountcode_tollfree_ratenum')
- : '';
- $eff_ratenum ||= $ratenum;
- $rate = qsearchs('rate', { 'ratenum' => $eff_ratenum })
- or die "ratenum $eff_ratenum not found!";
-
- my @ltime = localtime($cdr->startdate);
- $weektime = $ltime[0] +
- $ltime[1]*60 + #minutes
- $ltime[2]*3600 + #hours
- $ltime[6]*86400; #days since sunday
- # if there's no timed rate_detail for this time/region combination,
- # dest_detail returns the default. There may still be a timed rate
- # that applies after the starttime of the call, so be careful...
- $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
- 'phonenum' => $number,
- 'weektime' => $weektime,
- });
-
- if ( $rate_detail ) {
-
- $rate_region = $rate_detail->dest_region;
- $regionnum = $rate_region->regionnum;
- $regionname = $rate_region->regionname;
- warn " found rate for regionnum $regionnum ".
- "and rate detail $rate_detail\n"
- if $DEBUG;
-
- if ( !exists($interval_cache{$regionnum}) ) {
- my @intervals = (
- sort { $a->stime <=> $b->stime }
- map { my $r = $_->rate_time; $r ? $r->intervals : () }
- $rate->rate_detail
- );
- $interval_cache{$regionnum} = \@intervals;
- warn " cached ".scalar(@intervals)." interval(s)\n"
- if $DEBUG;
- }
-
- } elsif ( $ignore_unrateable ) {
-
- $rate_region = '';
- $regionnum = '';
- #code below will throw a warning & skip
-
- } else {
-
- die "FATAL: no rate_detail found in ".
- $rate->ratenum. ":". $rate->ratename. " rate plan ".
- "for +$countrycode $number (CDR acctid ". $cdr->acctid. "); ".
- "add a rate or set ignore_unrateable flag on the package def\n";
- }
-
- }
-
- } elsif ( $rating_method eq 'upstream_simple' ) {
-
- #XXX $charge = sprintf('%.2f', $cdr->upstream_price);
- $charge = sprintf('%.3f', $cdr->upstream_price);
- $charges += $charge;
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
-
- @call_details = ($cdr->downstream_csv( 'format' => $output_format,
- 'charge' => $charge,
- )
- );
- $classnum = $cdr->calltypenum;
-
- } elsif ( $rating_method eq 'single_price' ) {
-
- # a little false laziness w/below
- # $rate_detail = new FS::rate_detail({sec_granularity => ... }) ?
-
- my $granularity = length($self->option('sec_granularity'))
- ? $self->option('sec_granularity')
- : 60;
-
- $seconds = $use_duration ? $cdr->duration : $cdr->billsec;
-
- $seconds += $granularity - ( $seconds % $granularity )
- if $seconds # don't granular-ize 0 billsec calls (bills them)
- && $granularity # 0 is per call
- && $seconds % $granularity;
- my $minutes = $seconds / 60;
- # XXX config?
- #$charge = sprintf('%.2f', ( $self->option('min_charge') * $minutes )
- #+ 0.00000001 ); #so 1.005 rounds to 1.01
- $charge = sprintf('%.4f', ( $self->option('min_charge') * $minutes )
- + 0.0000000001 ); #so 1.00005 rounds to 1.0001
-
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
- $charges += $charge;
-
- @call_details = ($cdr->downstream_csv( 'format' => $output_format,
- 'charge' => $charge,
- 'seconds' => ($use_duration ?
- $cdr->duration :
- $cdr->billsec),
- 'granularity' => $granularity,
- )
- );
-
- } else {
- die "don't know how to rate CDRs using method: $rating_method\n";
- }
-
- ###
- # find the price and add detail to the invoice
- ###
-
- # if $rate_detail is not found, skip this CDR... i.e.
- # don't add it to invoice, don't set its status to done,
- # don't call downstream_csv or something on it...
- # but DO emit a warning...
- #if ( ! $rate_detail && ! scalar(@call_details) ) {}
- if ( ! $rate_detail && $charge eq '' ) {
-
- warn "no rate_detail found for CDR.acctid: ". $cdr->acctid.
- "; skipping\n"
-
- } else { # there *is* a rate_detail (or call_details), proceed...
- # About this section:
- # We don't round _anything_ (except granularizing)
- # until the final $charge = sprintf("%.2f"...).
-
- unless ( @call_details || ( $charge ne '' && $charge == 0 ) ) {
-
- my $seconds_left = $use_duration ? $cdr->duration : $cdr->billsec;
- # charge for the first (conn_sec) seconds
- $seconds = min($seconds_left, $rate_detail->conn_sec);
- $seconds_left -= $seconds;
- $weektime += $seconds;
- $charge = $rate_detail->conn_charge;
-
- my $etime;
- while($seconds_left) {
- my $ratetimenum = $rate_detail->ratetimenum; # may be empty
-
- # find the end of the current rate interval
- if(@{ $interval_cache{$regionnum} } == 0) {
- # There are no timed rates in this group, so just stay
- # in the default rate_detail for the entire duration.
- # Set an "end" of 1 past the end of the current call.
- $etime = $weektime + $seconds_left + 1;
- }
- elsif($ratetimenum) {
- # This is a timed rate, so go to the etime of this interval.
- # If it's followed by another timed rate, the stime of that
- # interval should match the etime of this one.
- my $interval = $rate_detail->rate_time->contains($weektime);
- $etime = $interval->etime;
- }
- else {
- # This is a default rate, so use the stime of the next
- # interval in the sequence.
- my $next_int = first { $_->stime > $weektime }
- @{ $interval_cache{$regionnum} };
- if ($next_int) {
- $etime = $next_int->stime;
- }
- else {
- # weektime is near the end of the week, so decrement
- # it by a full week and use the stime of the first
- # interval.
- $weektime -= (3600*24*7);
- $etime = $interval_cache{$regionnum}->[0]->stime;
- }
- }
-
- my $charge_sec = min($seconds_left, $etime - $weektime);
-
- $seconds_left -= $charge_sec;
-
- $included_min{$regionnum}{$ratetimenum} = $rate_detail->min_included
- unless exists $included_min{$regionnum}{$ratetimenum};
-
- my $granularity = $rate_detail->sec_granularity;
-
- my $minutes;
- if ( $granularity ) { # charge per minute
- # Round up to the nearest $granularity
- if ( $charge_sec and $charge_sec % $granularity ) {
- $charge_sec += $granularity - ($charge_sec % $granularity);
- }
- $minutes = $charge_sec / 60; #don't round this
- }
- else { # per call
- $minutes = 1;
- $seconds_left = 0;
- }
-
- $seconds += $charge_sec;
-
- $included_min{$regionnum}{$ratetimenum} -= $minutes;
- if ( $included_min{$regionnum}{$ratetimenum} <= 0 ) {
- my $charge_min = 0 - $included_min{$regionnum}{$ratetimenum}; #XXX should preserve
- #(display?) this
- $included_min{$regionnum}{$ratetimenum} = 0;
- $charge += ($rate_detail->min_charge * $charge_min); #still not rounded
- }
-
- # choose next rate_detail
- $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
- 'phonenum' => $number,
- 'weektime' => $etime })
- if($seconds_left);
- # we have now moved forward to $etime
- $weektime = $etime;
-
- } #while $seconds_left
- # this is why we need regionnum/rate_region....
- warn " (rate region $rate_region)\n" if $DEBUG;
-
- $classnum = $rate_detail->classnum;
- $charge = sprintf('%.2f', $charge + 0.000001); # NOW round it.
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
- $charges += $charge;
-
- @call_details = (
- $cdr->downstream_csv( 'format' => $output_format,
- 'granularity' => $rate_detail->sec_granularity,
- 'seconds' => ($use_duration ?
- $cdr->duration :
- $cdr->billsec),
- 'charge' => $charge,
- 'pretty_dst' => $pretty_destnum,
- 'dst_regionname' => $regionname,
- )
- );
- } #if(there is a rate_detail)
-
-
- if ( $charge > 0 ) {
- #just use FS::cust_bill_pkg_detail objects?
- my $call_details;
- my $phonenum = $svc_x->phonenum;
-
- if ( scalar(@call_details) == 1 ) {
- $call_details =
- [ 'C',
- $call_details[0],
- $charge,
- $classnum,
- $phonenum,
- $seconds,
- $regionname,
- ];
- } else { #only used for $rating_method eq 'upstream' now
- $csv->combine(@call_details);
- $call_details =
- [ 'C',
- $csv->string,
- $charge,
- $classnum,
- $phonenum,
- $seconds,
- $regionname,
- ];
- }
- warn " adding details on charge to invoice: [ ".
- join(', ', @{$call_details} ). " ]"
- if ( $DEBUG && ref($call_details) );
- push @$details, $call_details; #\@call_details,
- }
-
- # if the customer flag is on, call "downstream_csv" or something
- # like it to export the call downstream!
- # XXX price plan option to pick format, or something...
- #$downstream_cdr .= $cdr->downstream_csv( 'format' => 'XXX format' )
- # if $spool_cdr;
-
- my $error = $cdr->set_status_and_rated_price( 'done',
- $charge,
- $cust_svc->svcnum,
- );
- die $error if $error;
-
- }
-
- } # $cdr
-
- } # $cust_svc
-
- unshift @$details, [ 'C',
- FS::cdr::invoice_header($output_format),
- '',
- '',
- '',
- '',
- '',
- ]
- if @$details && $rating_method ne 'upstream';
-
-# if ( $spool_cdr && length($downstream_cdr) ) {
-#
-# use FS::UID qw(datasrc);
-# my $dir = '/usr/local/etc/freeside/export.'. datasrc. '/cdr';
-# mkdir $dir, 0700 unless -d $dir;
-# $dir .= '/'. $cust_pkg->custnum.
-# mkdir $dir, 0700 unless -d $dir;
-# my $filename = time2str("$dir/CDR%Y%m%d-spool.CSV", time); #XXX invoice date instead? would require changing the order things are generated in cust_main::bill insert cust_bill first - with transactions it could be done though
-#
-# push @{ $param->{'precommit_hooks'} },
-# sub {
-# #lock the downstream spool file and append the records
-# use Fcntl qw(:flock);
-# use IO::File;
-# my $spool = new IO::File ">>$filename"
-# or die "can't open $filename: $!\n";
-# flock( $spool, LOCK_EX)
-# or die "can't lock $filename: $!\n";
-# seek($spool, 0, 2)
-# or die "can't seek to end of $filename: $!\n";
-# print $spool $downstream_cdr;
-# flock( $spool, LOCK_UN );
-# close $spool;
-# };
-#
-# } #if ( $spool_cdr && length($downstream_cdr) )
-
- $charges;
-}
-
-#returns a reason why not to rate this CDR, or false if the CDR is chargeable
-sub check_chargable {
- my( $self, $cdr, %flags ) = @_;
-
- #should have some better way of checking these options from a hash
- #or something
-
- my @opt = qw(
- use_amaflags
- use_disposition
- use_disposition_taqua
- use_carrierid
- use_cdrtypenum
- skip_dst_prefix
- skip_dcontext
- skip_dstchannel_prefix
- skip_src_length_more noskip_src_length_accountcode_tollfree
- skip_dst_length_less noskip_dst_length_accountcode_tollfree
- skip_lastapp
- skip_max_callers
- );
- foreach my $opt (grep !exists($flags{option_cache}->{$_}), @opt ) {
- $flags{option_cache}->{$opt} = $self->option($opt, 1);
- }
- my %opt = %{ $flags{option_cache} };
-
- return 'amaflags != 2'
- if $opt{'use_amaflags'} && $cdr->amaflags != 2;
-
- return 'disposition != ANSWERED'
- if $opt{'use_disposition'} && $cdr->disposition ne 'ANSWERED';
-
- return "disposition != 100"
- if $opt{'use_disposition_taqua'} && $cdr->disposition != 100;
-
- return "carrierid != $opt{'use_carrierid'}"
- if length($opt{'use_carrierid'})
- && $cdr->carrierid ne $opt{'use_carrierid'} #ne otherwise 0 matches ''
- && ! $flags{'da_rewrote'};
-
- return "cdrtypenum != $opt{'use_cdrtypenum'}"
- if length($opt{'use_cdrtypenum'})
- && $cdr->cdrtypenum ne $opt{'use_cdrtypenum'}; #ne otherwise 0 matches ''
-
- foreach(split(',',$opt{'skip_dst_prefix'})) {
- return "dst starts with '$_'"
- if length($_) && substr($cdr->dst,0,length($_)) eq $_;
- }
-
- return "dcontext IN ( $opt{'skip_dcontext'} )"
- if $opt{'skip_dcontext'} =~ /\S/
- && grep { $cdr->dcontext eq $_ } split(/\s*,\s*/, $opt{'skip_dcontext'});
-
- my $len_prefix = length($opt{'skip_dstchannel_prefix'});
- return "dstchannel starts with $opt{'skip_dstchannel_prefix'}"
- if $len_prefix
- && substr($cdr->dstchannel,0,$len_prefix) eq $opt{'skip_dstchannel_prefix'};
-
- my $dst_length = $opt{'skip_dst_length_less'};
- return "destination less than $dst_length digits"
- if $dst_length && length($cdr->dst) < $dst_length
- && ! ( $opt{'noskip_dst_length_accountcode_tollfree'}
- && $cdr->is_tollfree('accountcode')
- );
-
- return "lastapp is $opt{'skip_lastapp'}"
- if length($opt{'skip_lastapp'}) && $cdr->lastapp eq $opt{'skip_lastapp'};
-
- my $src_length = $opt{'skip_src_length_more'};
- if ( $src_length ) {
-
- if ( $opt{'noskip_src_length_accountcode_tollfree'} ) {
-
- if ( $cdr->is_tollfree('accountcode') ) {
- return "source less than or equal to $src_length digits"
- if length($cdr->src) <= $src_length;
- } else {
- return "source more than $src_length digits"
- if length($cdr->src) > $src_length;
- }
-
- } else {
- return "source more than $src_length digits"
- if length($cdr->src) > $src_length;
- }
-
- }
-
- return "max_callers <= $opt{skip_max_callers}"
- if length($opt{'skip_max_callers'})
- and length($cdr->max_callers)
- and $cdr->max_callers <= $opt{'skip_max_callers'};
-
- #all right then, rate it
- '';
-}
-
-sub is_free {
- 0;
-}
-
-# This equates svc_phone records; perhaps svc_phone should have a field
-# to indicate it represents a line
-sub calc_units {
- my($self, $cust_pkg ) = @_;
- my $count = 0;
- if ( $self->option('count_available_phones', 1)) {
- map { $count += ( $_->quantity || 0 ) }
- grep { $_->part_svc->svcdb eq 'svc_phone' }
- $cust_pkg->part_pkg->pkg_svc;
- } else {
- $count =
- scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc);
- }
- $count;
-}
-
-1;
-
diff --git a/FS/FS/part_pkg/voip_inbound.pm b/FS/FS/part_pkg/voip_inbound.pm
deleted file mode 100644
index 1b91575..0000000
--- a/FS/FS/part_pkg/voip_inbound.pm
+++ /dev/null
@@ -1,366 +0,0 @@
-package FS::part_pkg::voip_inbound;
-
-use strict;
-use vars qw(@ISA $DEBUG %info);
-use Date::Format;
-use Tie::IxHash;
-use FS::Conf;
-use FS::Record qw(qsearchs qsearch);
-use FS::part_pkg::recur_Common;
-use FS::cdr;
-use FS::part_pkg::recur_Common;
-
-@ISA = qw(FS::part_pkg::recur_Common);
-
-$DEBUG = 0;
-
-tie my %temporalities, 'Tie::IxHash',
- 'upcoming' => "Upcoming (future)",
- 'preceding' => "Preceding (past)",
-;
-
-tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
-
-%info = (
- 'name' => 'VoIP flat rate pricing of CDRs for inbound calls',
- 'shortname' => 'VoIP/telco CDR rating (inbound)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- #false laziness w/flat.pm
- 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
- 'type' => 'select',
- 'select_options' => \%temporalities,
- },
- 'cutoff_day' => { 'name' => 'Billing Day (1 - 28) for prorating or '.
- 'subscription',
- 'default' => '1',
- },
- 'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
- 'for one full period after that',
- 'type' => 'checkbox',
- },
-
- 'recur_method' => { 'name' => 'Recurring fee method',
- 'type' => 'select',
- 'select_options' => \%FS::part_pkg::recur_Common::recur_method,
- },
-
- 'min_charge' => { 'name' => 'Charge per minute',
- },
-
- 'sec_granularity' => { 'name' => 'Granularity',
- 'type' => 'select',
- 'select_options' => \%granularity,
- },
-
- 'default_prefix' => { 'name' => 'Default prefix optionally prepended to customer DID numbers when searching for CDR records',
- 'default' => '+1',
- },
-
- 'disable_tollfree' => { 'name' => 'Disable automatic toll-free processing',
- 'type' => 'checkbox',
- },
-
- 'use_amaflags' => { 'name' => 'Do not charge for CDRs where the amaflags field is not set to "2" ("BILL"/"BILLING").',
- 'type' => 'checkbox',
- },
-
- 'use_disposition' => { 'name' => 'Do not charge for CDRs where the disposition flag is not set to "ANSWERED".',
- 'type' => 'checkbox',
- },
-
- 'use_disposition_taqua' => { 'name' => 'Do not charge for CDRs where the disposition is not set to "100" (Taqua).',
- 'type' => 'checkbox',
- },
-
- 'use_carrierid' => { 'name' => 'Do not charge for CDRs where the Carrier ID is not set to: ',
- },
-
- 'use_cdrtypenum' => { 'name' => 'Do not charge for CDRs where the CDR Type is not set to: ',
- },
-
- 'skip_dcontext' => { 'name' => 'Do not charge for CDRs where the dcontext is set to any of these (comma-separated) values:',
- },
-
- 'skip_dstchannel_prefix' => { 'name' => 'Do not charge for CDRs where the dstchannel starts with:',
- },
-
- 'skip_dst_length_less' => { 'name' => 'Do not charge for CDRs where the destination is less than this many digits:',
- },
-
- 'skip_lastapp' => { 'name' => 'Do not charge for CDRs where the lastapp matches this value',
- },
-
- 'use_duration' => { 'name' => 'Calculate usage based on the duration field instead of the billsec field',
- 'type' => 'checkbox',
- },
-
- #false laziness w/cdr_termination.pm
- 'output_format' => { 'name' => 'CDR invoice display format',
- 'type' => 'select',
- 'select_options' => { FS::cdr::invoice_formats() },
- 'default' => 'default', #XXX test
- },
-
- 'usage_section' => { 'name' => 'Section in which to place usage charges (whether separated or not)',
- },
-
- 'summarize_usage' => { 'name' => 'Include usage summary with recurring charges when usage is in separate section',
- 'type' => 'checkbox',
- },
-
- 'usage_mandate' => { 'name' => 'Always put usage details in separate section',
- 'type' => 'checkbox',
- },
- #eofalse
-
- 'bill_every_call' => { 'name' => 'Generate an invoice immediately for every call. Useful for prepaid.',
- 'type' => 'checkbox',
- },
-
- #XXX also have option for an external db
-# 'cdr_location' => { 'name' => 'CDR database location'
-# 'type' => 'select',
-# 'select_options' => \%cdr_location,
-# 'select_callback' => {
-# 'external' => {
-# 'enable' => [ 'datasrc', 'username', 'password' ],
-# },
-# 'internal' => {
-# 'disable' => [ 'datasrc', 'username', 'password' ],
-# }
-# },
-# },
-# 'datasrc' => { 'name' => 'DBI data source for external CDR table',
-# 'disabled' => 'Y',
-# },
-# 'username' => { 'name' => 'External database username',
-# 'disabled' => 'Y',
-# },
-# 'password' => { 'name' => 'External database password',
-# 'disabled' => 'Y',
-# },
-
- },
- 'fieldorder' => [qw(
- recur_temporality
- recur_method cutoff_day add_full_period
- min_charge sec_granularity
- default_prefix
- disable_tollfree
- use_amaflags use_disposition
- use_disposition_taqua use_carrierid use_cdrtypenum
- skip_dcontext skip_dstchannel_prefix
- skip_dst_length_less skip_lastapp
- use_duration
- output_format usage_mandate summarize_usage usage_section
- bill_every_call
- )
- ],
- 'weight' => 40,
-);
-
-sub calc_setup {
- my($self, $cust_pkg ) = @_;
- $self->option('setup_fee');
-}
-
-sub calc_recur {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- my $charges = 0;
-
- $charges += $self->calc_usage(@_);
- $charges += $self->calc_recur_Common(@_);
-
- $charges;
-
-}
-
-sub calc_cancel {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- $self->calc_usage(@_);
-}
-
-#false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again
-
-sub calc_usage {
- my $self = shift;
- my($cust_pkg, $sdate, $details, $param ) = @_;
-
- #my $last_bill = $cust_pkg->last_bill;
- my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
-
- return 0
- if $self->option('recur_temporality', 1) eq 'preceding'
- && ( $last_bill eq '' || $last_bill == 0 );
-
- my $spool_cdr = $cust_pkg->cust_main->spool_cdr;
-
- my %included_min = ();
-
- my $charges = 0;
-
-# my $downstream_cdr = '';
-
- my $disable_tollfree = $self->option('disable_tollfree');
- my $ignore_unrateable = $self->option('ignore_unrateable', 'Hush!');
- my $use_duration = $self->option('use_duration');
-
- my $output_format = $self->option('output_format', 'Hush!') || 'default';
-
- #for check_chargable, so we don't keep looking up options inside the loop
- my %opt_cache = ();
-
- eval "use Text::CSV_XS;";
- die $@ if $@;
- my $csv = new Text::CSV_XS;
-
- foreach my $cust_svc (
- grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc
- ) {
- my $svc_phone = $cust_svc->svc_x;
-
- foreach my $cdr ( $svc_phone->get_cdrs(
- 'for_update' => 1,
- 'status' => '', # unprocessed only
- 'default_prefix' => $self->option('default_prefix'),
- 'inbound' => 1,
- )
- ) {
- if ( $DEBUG > 1 ) {
- warn "rating inbound CDR $cdr\n".
- join('', map { " $_ => ". $cdr->{$_}. "\n" } keys %$cdr );
- }
- my $granularity = length($self->option('sec_granularity'))
- ? $self->option('sec_granularity')
- : 60;
-
- my $seconds = $use_duration ? $cdr->duration : $cdr->billsec;
-
- $seconds += $granularity - ( $seconds % $granularity )
- if $seconds # don't granular-ize 0 billsec calls (bills them)
- && $granularity; # 0 is per call
- my $minutes = sprintf("%.1f",$seconds / 60);
- $minutes =~ s/\.0$// if $granularity == 60; # count whole minutes, convert to integer
- $minutes = 1 unless $granularity; # per call
- my $charge = sprintf('%.2f', ( $self->option('min_charge') * $minutes )
- + 0.00000001 ); #so 1.00005 rounds to 1.0001
- next if !$charge;
- $charges += $charge;
- my @call_details = ($cdr->downstream_csv( 'format' => $output_format,
- 'charge' => $charge,
- 'minutes' => $minutes,
- 'granularity' => $granularity,
- )
- );
- push @$details,
- [ 'C',
- $call_details[0],
- $charge,
- $cdr->calltypenum, #classnum
- $self->phonenum,
- $seconds,
- '', #regionname, not set for inbound calls
- ];
-
- my $error = $cdr->set_status_and_rated_price( 'done',
- $charge,
- $cust_svc->svcnum,
- 'inbound' => 1 );
- die $error if $error;
-
- } #$cdr
- } # $cust_svc
- unshift @$details, [ 'C',
- FS::cdr::invoice_header($output_format),
- '',
- '',
- '',
- '',
- '',
- ]
- if @$details;
-
- $charges;
-}
-
-#returns a reason why not to rate this CDR, or false if the CDR is chargeable
-sub check_chargable {
- my( $self, $cdr, %flags ) = @_;
-
- #should have some better way of checking these options from a hash
- #or something
-
- my @opt = qw(
- use_amaflags
- use_disposition
- use_disposition_taqua
- use_carrierid
- use_cdrtypenum
- skip_dcontext
- skip_dstchannel_prefix
- skip_dst_length_less
- skip_lastapp
- );
- foreach my $opt (grep !exists($flags{option_cache}->{$_}), @opt ) {
- $flags{option_cache}->{$opt} = $self->option($opt, 1);
- }
- my %opt = %{ $flags{option_cache} };
-
- return 'amaflags != 2'
- if $opt{'use_amaflags'} && $cdr->amaflags != 2;
-
- return 'disposition != ANSWERED'
- if $opt{'use_disposition'} && $cdr->disposition ne 'ANSWERED';
-
- return "disposition != 100"
- if $opt{'use_disposition_taqua'} && $cdr->disposition != 100;
-
- return "carrierid != $opt{'use_carrierid'}"
- if length($opt{'use_carrierid'})
- && $cdr->carrierid ne $opt{'use_carrierid'} #ne otherwise 0 matches ''
- && ! $flags{'da_rewrote'};
-
- return "cdrtypenum != $opt{'use_cdrtypenum'}"
- if length($opt{'use_cdrtypenum'})
- && $cdr->cdrtypenum ne $opt{'use_cdrtypenum'}; #ne otherwise 0 matches ''
-
- return "dcontext IN ( $opt{'skip_dcontext'} )"
- if $opt{'skip_dcontext'} =~ /\S/
- && grep { $cdr->dcontext eq $_ } split(/\s*,\s*/, $opt{'skip_dcontext'});
-
- my $len_prefix = length($opt{'skip_dstchannel_prefix'});
- return "dstchannel starts with $opt{'skip_dstchannel_prefix'}"
- if $len_prefix
- && substr($cdr->dstchannel,0,$len_prefix) eq $opt{'skip_dstchannel_prefix'};
-
- my $dst_length = $opt{'skip_dst_length_less'};
- return "destination less than $dst_length digits"
- if $dst_length && length($cdr->dst) < $dst_length;
-
- return "lastapp is $opt{'skip_lastapp'}"
- if length($opt{'skip_lastapp'}) && $cdr->lastapp eq $opt{'skip_lastapp'};
-
- #all right then, rate it
- '';
-}
-
-sub is_free {
- 0;
-}
-
-# This equates svc_phone records; perhaps svc_phone should have a field
-# to indicate it represents a line
-sub calc_units {
- my($self, $cust_pkg ) = @_;
- my $count =
- scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc);
- $count;
-}
-
-1;
-
diff --git a/FS/FS/part_pkg/voip_sqlradacct.pm b/FS/FS/part_pkg/voip_sqlradacct.pm
deleted file mode 100644
index 5388767..0000000
--- a/FS/FS/part_pkg/voip_sqlradacct.pm
+++ /dev/null
@@ -1,185 +0,0 @@
-package FS::part_pkg::voip_sqlradacct;
-
-use strict;
-use vars qw(@ISA $DEBUG %info);
-use Date::Format;
-use FS::Record qw(qsearchs qsearch);
-use FS::part_pkg::flat;
-#use FS::rate;
-use FS::rate_prefix;
-
-@ISA = qw(FS::part_pkg::flat);
-
-$DEBUG = 1;
-
-%info = (
- 'disabled' => 1, #they're sucked into our CDR table now instead
- 'name' => 'VoIP rating by plan of CDR records in an SQL RADIUS radacct table',
- 'shortname' => 'VoIP/telco CDR rating (external RADIUS)',
- 'inherit_fields' => [ 'global_Mixin' ],
- 'fields' => {
- 'ratenum' => { 'name' => 'Rate plan',
- 'type' => 'select',
- 'select_table' => 'rate',
- 'select_key' => 'ratenum',
- 'select_label' => 'ratename',
- },
- },
- 'fieldorder' => [qw( ratenum ignore_unrateable )],
- 'weight' => 40,
-);
-
-sub calc_setup {
- my($self, $cust_pkg ) = @_;
- $self->option('setup_fee');
-}
-
-#false laziness w/voip_cdr... resolve it if this one ever gets used again
-sub calc_recur {
- my($self, $cust_pkg, $sdate, $details ) = @_;
-
- my $last_bill = $cust_pkg->last_bill;
-
- my $ratenum = $cust_pkg->part_pkg->option('ratenum');
-
- my %included_min = ();
-
- my $charges = 0;
-
- foreach my $cust_svc (
- grep { $_->part_svc->svcdb eq 'svc_acct' } $cust_pkg->cust_svc
- ) {
-
- foreach my $session (
- $cust_svc->get_session_history( $last_bill, $$sdate )
- ) {
- if ( $DEBUG > 1 ) {
- warn "rating session $session\n".
- join('', map { " $_ => ". $session->{$_}. "\n" } keys %$session );
- }
-
- ###
- # look up rate details based on called station id
- ###
-
- my $dest = $session->{'calledstationid'};
-
- #remove non-phone# stuff and whitespace
- $dest =~ s/\s//g;
- my $proto = '';
- $dest =~ s/^(\w+):// and $proto = $1; #sip:
- my $siphost = '';
- $dest =~ s/\@(.*)$// and $siphost = $1; # @10.54.32.1, @sip.example.com
-
- #determine the country code
- my $countrycode;
- if ( $dest =~ /^011(((\d)(\d))(\d))(\d+)$/ ) {
-
- my( $three, $two, $one, $u1, $u2, $rest ) = ( $1, $2, $3, $4, $5, $6 );
- #first look for 1 digit country code
- if ( qsearch('rate_prefix', { 'countrycode' => $one } ) ) {
- $countrycode = $one;
- $dest = $u1.$u2.$rest;
- } elsif ( qsearch('rate_prefix', { 'countrycode' => $two } ) ) { #or 2
- $countrycode = $two;
- $dest = $u2.$rest;
- } else { #3 digit country code
- $countrycode = $three;
- $dest = $rest;
- }
-
- } else {
- $countrycode = '1';
- $dest =~ s/^1//;# if length($dest) > 10;
- }
-
- warn "rating call to +$countrycode $dest\n" if $DEBUG;
-
- #find a rate prefix, first look at most specific (4 digits) then 3, etc.,
- # finally trying the country code only
- my $rate_prefix = '';
- for my $len ( reverse(1..6) ) {
- $rate_prefix = qsearchs('rate_prefix', {
- 'countrycode' => $countrycode,
- #'npa' => { op=> 'LIKE', value=> substr($dest, 0, $len) }
- 'npa' => substr($dest, 0, $len),
- } ) and last;
- }
- $rate_prefix ||= qsearchs('rate_prefix', {
- 'countrycode' => $countrycode,
- 'npa' => '',
- });
-
- die "Can't find rate for call to +$countrycode $dest\n"
- unless $rate_prefix;
-
- my $regionnum = $rate_prefix->regionnum;
- my $rate_detail = qsearchs('rate_detail', {
- 'ratenum' => $ratenum,
- 'dest_regionnum' => $regionnum,
- } );
-
- warn " found rate for regionnum $regionnum ".
- "and rate detail $rate_detail\n"
- if $DEBUG;
-
- ###
- # find the price and add detail to the invoice
- ###
-
- $included_min{$regionnum} = $rate_detail->min_included
- unless exists $included_min{$regionnum};
-
- my $granularity = $rate_detail->sec_granularity;
- my $seconds = $session->{'acctsessiontime'};
- $seconds += $granularity - ( $seconds % $granularity );
- my $minutes = sprintf("%.1f", $seconds / 60);
- $minutes =~ s/\.0$// if $granularity == 60;
-
- $included_min{$regionnum} -= $minutes;
-
- my $charge = 0;
- if ( $included_min{$regionnum} < 0 ) {
- my $charge_min = 0 - $included_min{$regionnum};
- $included_min{$regionnum} = 0;
- $charge = sprintf('%.2f', $rate_detail->min_charge * $charge_min );
- $charges += $charge;
- }
-
- my $rate_region = $rate_prefix->rate_region;
- warn " (rate region $rate_region)\n" if $DEBUG;
-
- my @call_details = (
- #time2str("%Y %b %d - %r", $session->{'acctstarttime'}),
- time2str("%c", $session->{'acctstarttime'}),
- $minutes.'m',
- '$'.$charge,
- "+$countrycode $dest",
- $rate_region->regionname,
- );
-
- warn " adding details on charge to invoice: ".
- join(' - ', @call_details )
- if $DEBUG;
-
- push @$details, join(' - ', @call_details); #\@call_details,
-
- } # $session
-
- } # $cust_svc
-
- $self->option('recur_fee') + $charges;
-
-}
-
-sub can_discount { 0; }
-
-sub is_free { 0; }
-
-sub base_recur {
- my($self, $cust_pkg) = @_;
- $self->option('recur_fee');
-}
-
-1;
-