From 9b6b116d4a492cb0edc31e53abf1fdb43be42d0e Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 31 Jan 2010 02:57:14 +0000 Subject: [PATCH] discounts, RT#6679 --- FS/FS/cust_pkg_discount.pm | 18 +++++++++++ FS/FS/part_pkg/agent.pm | 2 ++ FS/FS/part_pkg/bulk.pm | 2 ++ FS/FS/part_pkg/flat.pm | 58 +++++++++++++++++++++++++++++++++-- FS/FS/part_pkg/flat_comission.pm | 2 ++ FS/FS/part_pkg/flat_comission_cust.pm | 2 ++ FS/FS/part_pkg/flat_comission_pkg.pm | 2 ++ FS/FS/part_pkg/prorate.pm | 9 ++++-- FS/FS/part_pkg/recur_Common.pm | 2 ++ FS/FS/part_pkg/rt_time.pm | 2 ++ FS/FS/part_pkg/sesmon_hour.pm | 2 ++ FS/FS/part_pkg/sesmon_minute.pm | 2 ++ FS/FS/part_pkg/sql_external.pm | 6 ++-- FS/FS/part_pkg/sql_generic.pm | 2 ++ FS/FS/part_pkg/sqlradacct_hour.pm | 2 ++ FS/FS/part_pkg/subscription.pm | 6 +++- FS/FS/part_pkg/voip_sqlradacct.pm | 6 ++-- 17 files changed, 114 insertions(+), 11 deletions(-) diff --git a/FS/FS/cust_pkg_discount.pm b/FS/FS/cust_pkg_discount.pm index 87f8c5283..9fc618cab 100644 --- a/FS/FS/cust_pkg_discount.pm +++ b/FS/FS/cust_pkg_discount.pm @@ -133,6 +133,8 @@ sub check { =item cust_pkg +Returns the customer package (see L). + =cut sub cust_pkg { @@ -142,6 +144,8 @@ sub cust_pkg { =item discount +Returns the discount (see L). + =cut sub discount { @@ -149,6 +153,20 @@ sub discount { qsearchs('discount', { 'discountnum' => $self->discountnum } ); } +=item increment_months_used + +Increments months_used by the given parameter + +=cut + +sub increment_months_used { + my( $self, $used ) = @_; + #UPDATE cust_pkg_discount SET months_used = months_used + ? + #leaves no history, and billing is mutexed per-customer, so the dum way is ok + $self->months_used( $self->months_used + $used ); + $self->replace(); +} + =back =head1 BUGS diff --git a/FS/FS/part_pkg/agent.pm b/FS/FS/part_pkg/agent.pm index d41978cf5..e5bd163f1 100644 --- a/FS/FS/part_pkg/agent.pm +++ b/FS/FS/part_pkg/agent.pm @@ -163,6 +163,8 @@ sub calc_recur { } +sub can_discount { 0; } + sub hide_svc_detail { 1; } diff --git a/FS/FS/part_pkg/bulk.pm b/FS/FS/part_pkg/bulk.pm index 1b52d9fc3..69fe98e92 100644 --- a/FS/FS/part_pkg/bulk.pm +++ b/FS/FS/part_pkg/bulk.pm @@ -94,6 +94,8 @@ sub calc_recur { sprintf('%.2f', $self->base_recur($cust_pkg) + $total_svc_charge ); } +sub can_discount { 0; } + sub hide_svc_detail { 1; } diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm index 9bb45e624..402730d3f 100644 --- a/FS/FS/part_pkg/flat.pm +++ b/FS/FS/part_pkg/flat.pm @@ -6,8 +6,10 @@ use vars qw( @ISA %info @usage_fieldorder @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); @@ -153,7 +155,55 @@ sub calc_recur { return 0 if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0; - $self->base_recur(@_); + my $br = $self->base_recur(@_); + + my $discount = $self->calc_discount(@_); + + sprintf('%.2f', $br - $discount); +} + +sub calc_discount { + my $self = shift; + my($cust_pkg, $sdate, $details, $param ) = @_; + + my $br = $self->base_recur(@_); + + my $tot_discount = 0; + #UI enforces just 1 for now, will need ordering when they can be stacked + foreach my $cust_pkg_discount ( $cust_pkg->cust_pkg_discount_active ) { + my $discount = $cust_pkg_discount->discount; + #UI enforces one or the other (for now? probably for good) + my $amount = 0; + $amount += $discount->amount; + $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->months_used ) + : $chg_months; + + my $error = $cust_pkg_discount->increment_months_used($months); + die "error discounting: $error" if $error; + + $amount *= $months; + + #add details on discount to invoice + my $conf = new FS::Conf; + my $money_char = $conf->config('money_char') || '$'; + + 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); } sub base_recur { @@ -212,7 +262,11 @@ sub is_free_options { sub is_prepaid { 0; } #no, we're postpaid -sub can_discount { 1; } #and anything that inherits from us +#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; diff --git a/FS/FS/part_pkg/flat_comission.pm b/FS/FS/part_pkg/flat_comission.pm index 1f57e4f9e..c05e45586 100644 --- a/FS/FS/part_pkg/flat_comission.pm +++ b/FS/FS/part_pkg/flat_comission.pm @@ -64,4 +64,6 @@ sub calc_recur { $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 index e2034cd88..5db46585e 100644 --- a/FS/FS/part_pkg/flat_comission_cust.pm +++ b/FS/FS/part_pkg/flat_comission_cust.pm @@ -62,4 +62,6 @@ sub calc_recur { $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 index 0a66ff03f..6f5ee692c 100644 --- a/FS/FS/part_pkg/flat_comission_pkg.pm +++ b/FS/FS/part_pkg/flat_comission_pkg.pm @@ -55,4 +55,6 @@ sub calc_recur { $self->option('recur_fee'); } +sub can_discount { 0; } + 1; diff --git a/FS/FS/part_pkg/prorate.pm b/FS/FS/part_pkg/prorate.pm index d3ca77ae9..894666d2c 100644 --- a/FS/FS/part_pkg/prorate.pm +++ b/FS/FS/part_pkg/prorate.pm @@ -95,7 +95,7 @@ use FS::part_pkg::flat; ); sub calc_recur { - my($self, $cust_pkg, $sdate ) = @_; + 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]; @@ -117,7 +117,12 @@ sub calc_recur { $$sdate = $mstart; my $permonth = $self->option('recur_fee') / $self->freq; - $permonth * ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) ); + my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) ); + + $param->{'months'} = $months; + my $discount = $self->calc_discount( $cust_pkg, $sdate, $details, $param); + + sprintf('%.2f', $permonth * $months - $discount); } 1; diff --git a/FS/FS/part_pkg/recur_Common.pm b/FS/FS/part_pkg/recur_Common.pm index 2739cbc8f..8ed9eb6af 100644 --- a/FS/FS/part_pkg/recur_Common.pm +++ b/FS/FS/part_pkg/recur_Common.pm @@ -48,6 +48,8 @@ sub calc_recur_Common { }#$recur_method eq 'subscription' + $charges -= $self->calc_discount( $cust_pkg, $sdate, $details, $param ); + }#$recur_method eq 'prorate' }#increment_next_bill diff --git a/FS/FS/part_pkg/rt_time.pm b/FS/FS/part_pkg/rt_time.pm index 675a466b3..9452d4402 100644 --- a/FS/FS/part_pkg/rt_time.pm +++ b/FS/FS/part_pkg/rt_time.pm @@ -39,6 +39,8 @@ sub calc_recur { } +sub can_discount { 0; } + sub calc_cancel { my $self = shift; my($cust_pkg, $sdate, $details, $param ) = @_; diff --git a/FS/FS/part_pkg/sesmon_hour.pm b/FS/FS/part_pkg/sesmon_hour.pm index 22ece9557..e608d5510 100644 --- a/FS/FS/part_pkg/sesmon_hour.pm +++ b/FS/FS/part_pkg/sesmon_hour.pm @@ -45,6 +45,8 @@ sub calc_recur { } +sub can_discount { 0; } + sub is_free_options { qw( setup_fee recur_fee recur_hourly_charge ); } diff --git a/FS/FS/part_pkg/sesmon_minute.pm b/FS/FS/part_pkg/sesmon_minute.pm index 7386df675..654fdf596 100644 --- a/FS/FS/part_pkg/sesmon_minute.pm +++ b/FS/FS/part_pkg/sesmon_minute.pm @@ -44,6 +44,8 @@ sub calc_recur { $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 ); } diff --git a/FS/FS/part_pkg/sql_external.pm b/FS/FS/part_pkg/sql_external.pm index 70f9f048a..786600d06 100644 --- a/FS/FS/part_pkg/sql_external.pm +++ b/FS/FS/part_pkg/sql_external.pm @@ -65,9 +65,9 @@ sub calc_recur { $price; } -sub is_free { - 0; -} +sub can_discount { 0; } + +sub is_free { 0; } sub base_recur { my($self, $cust_pkg) = @_; diff --git a/FS/FS/part_pkg/sql_generic.pm b/FS/FS/part_pkg/sql_generic.pm index 5a6a11ab0..eb8004476 100644 --- a/FS/FS/part_pkg/sql_generic.pm +++ b/FS/FS/part_pkg/sql_generic.pm @@ -76,6 +76,8 @@ sub calc_recur { $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 ); } diff --git a/FS/FS/part_pkg/sqlradacct_hour.pm b/FS/FS/part_pkg/sqlradacct_hour.pm index c86956a44..15f678f0e 100644 --- a/FS/FS/part_pkg/sqlradacct_hour.pm +++ b/FS/FS/part_pkg/sqlradacct_hour.pm @@ -158,6 +158,8 @@ sub calc_recur { $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 ); diff --git a/FS/FS/part_pkg/subscription.pm b/FS/FS/part_pkg/subscription.pm index dbf6d797d..825f5cad6 100644 --- a/FS/FS/part_pkg/subscription.pm +++ b/FS/FS/part_pkg/subscription.pm @@ -103,7 +103,11 @@ sub calc_recur { $$sdate = timelocal(0,0,0,$cutoff_day,$mon,$year); - $self->option('recur_fee'); + my $br = $self->base_recur(@_); + + my $discount = $self->calc_discount(@_); + + sprintf('%.2f', $br - $discount); } 1; diff --git a/FS/FS/part_pkg/voip_sqlradacct.pm b/FS/FS/part_pkg/voip_sqlradacct.pm index b4f0cf9b5..441df587b 100644 --- a/FS/FS/part_pkg/voip_sqlradacct.pm +++ b/FS/FS/part_pkg/voip_sqlradacct.pm @@ -181,9 +181,9 @@ sub calc_recur { } -sub is_free { - 0; -} +sub can_discount { 0; } + +sub is_free { 0; } sub base_recur { my($self, $cust_pkg) = @_; -- 2.11.0