[freeside-commits] branch FREESIDE_4_BRANCH updated. 05cc1ce102266aa0a25e085b0b39d777686210a6
Mark Wells
mark at 420.am
Fri Oct 16 15:52:03 PDT 2015
The branch, FREESIDE_4_BRANCH has been updated
via 05cc1ce102266aa0a25e085b0b39d777686210a6 (commit)
from 8f086c4f68684d696ab64ccfab797272111d3ce4 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 05cc1ce102266aa0a25e085b0b39d777686210a6
Author: Mark Wells <mark at freeside.biz>
Date: Fri Oct 16 15:32:32 2015 -0700
separate setup and recur discounts, #14092
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 486860f..5240424 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2888,6 +2888,7 @@ sub tables_hashref {
'otaker', 'varchar', 'NULL', 32, '', '',
'usernum', 'int', 'NULL', '', '', '',
'disabled', 'char', 'NULL', 1, '', '',
+ 'setuprecur', 'char', 'NULL', 5, '', '',
],
'primary_key' => 'pkgdiscountnum',
'unique' => [],
diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm
index 1a3217c..ffaef97 100644
--- a/FS/FS/Template_Mixin.pm
+++ b/FS/FS/Template_Mixin.pm
@@ -3050,6 +3050,9 @@ sub _items_cust_bill_pkg {
# if the current line item is waiting to go out, and the one we're about
# to start is not bundled, then push out the current one and start a new
# one.
+ if ( $d ) {
+ $d->{amount} = $d->{setup_amount} + $d->{recur_amount};
+ }
foreach ( $s, $r, ($opt{skip_usage} ? () : $u ), $d ) {
if ( $_ && !$cust_bill_pkg->hidden ) {
$_->{amount} = sprintf( "%.2f", $_->{amount} );
@@ -3485,7 +3488,8 @@ sub _items_cust_bill_pkg {
# $item_discount->{amount} is negative
if ( $d and $cust_bill_pkg->hidden ) {
- $d->{amount} += $item_discount->{amount};
+ $d->{setup_amount} += $item_discount->{setup_amount};
+ $d->{recur_amount} += $item_discount->{recur_amount};
} else {
$d = $item_discount;
$_ = &{$escape_function}($_) foreach @{ $d->{ext_description} };
@@ -3493,27 +3497,9 @@ sub _items_cust_bill_pkg {
# update the active line (before the discount) to show the
# original price (whether this is a hidden line or not)
- #
- # quotation discounts keep track of setup and recur; invoice
- # discounts currently don't
- if ( exists $item_discount->{setup_amount} ) {
-
- $s->{amount} -= $item_discount->{setup_amount} if $s;
- $r->{amount} -= $item_discount->{recur_amount} if $r;
- } else {
-
- # $active_line is the line item hashref for the line that will
- # show the original price
- # (use the recur or single line for the package, unless we're
- # showing a setup line for a package with no recurring fee)
- my $active_line = $r;
- if ( $type eq 'S' ) {
- $active_line = $s;
- }
- $active_line->{amount} -= $item_discount->{amount};
-
- }
+ $s->{amount} -= $item_discount->{setup_amount} if $s;
+ $r->{amount} -= $item_discount->{recur_amount} if $r;
} # if there are any discounts
} # if this is an appropriate place to show discounts
@@ -3522,6 +3508,11 @@ sub _items_cust_bill_pkg {
}
+ # discount amount is internally split up
+ if ( $d ) {
+ $d->{amount} = $d->{setup_amount} + $d->{recur_amount};
+ }
+
foreach ( $s, $r, ($opt{skip_usage} ? () : $u ), $d ) {
if ( $_ ) {
$_->{amount} = sprintf( "%.2f", $_->{amount} ),
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 1780426..5861ee4 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -820,6 +820,8 @@ quantity.
sub _item_discount {
my $self = shift;
+ my %options = @_;
+
my @pkg_discounts = $self->pkg_discount;
return if @pkg_discounts == 0;
# special case: if there are old "discount details" on this line item, don't
@@ -832,7 +834,8 @@ sub _item_discount {
my $d = {
_is_discount => 1,
description => $self->mt('Discount'),
- amount => 0,
+ setup_amount => 0,
+ recur_amount => 0,
ext_description => \@ext,
pkgpart => $self->pkgpart,
feepart => $self->feepart,
@@ -840,9 +843,11 @@ sub _item_discount {
};
foreach my $pkg_discount (@pkg_discounts) {
push @ext, $pkg_discount->description;
- $d->{amount} -= $pkg_discount->amount;
+ my $setuprecur = $pkg_discount->cust_pkg_discount->setuprecur;
+ $d->{$setuprecur.'_amount'} -= $pkg_discount->amount;
}
- $d->{amount} *= $self->quantity || 1;
+ $d->{setup_amount} *= $self->quantity || 1; # ??
+ $d->{recur_amount} *= $self->quantity || 1; # ??
return $d;
}
diff --git a/FS/FS/cust_bill_pkg_discount.pm b/FS/FS/cust_bill_pkg_discount.pm
index 9e64d20..616657a 100644
--- a/FS/FS/cust_bill_pkg_discount.pm
+++ b/FS/FS/cust_bill_pkg_discount.pm
@@ -135,10 +135,36 @@ Returns a string describing the discount (for use on an invoice).
sub description {
my $self = shift;
my $discount = $self->cust_pkg_discount->discount;
+
+ if ( $self->months == 0 ) {
+ # then this is a setup discount
+ my $desc = $discount->name;
+ if ( $desc ) {
+ $desc .= ': ';
+ } else {
+ $desc = $self->mt('Setup discount of ');
+ }
+ if ( (my $percent = $discount->percent) > 0 ) {
+ $percent = sprintf('%.1f', $percent) if $percent > int($percent);
+ $percent =~ s/\.0+$//;
+ $desc .= $percent . '%';
+ } else {
+ # note "$self->amount", not $discount->amount. if a flat discount
+ # is applied to the setup fee, show the amount actually discounted.
+ # we might do this for all types of discounts.
+ my $money_char = FS::Conf->new->config('money_char') || '$';
+ $desc .= $money_char . sprintf('%.2f', $self->amount);
+ }
+
+ # don't show "/month", months remaining or used, etc., as for setup
+ # discounts it doesn't matter.
+ return $desc;
+ }
+
my $desc = $discount->description_short;
$desc .= $self->mt(' each') if $self->cust_bill_pkg->quantity > 1;
- if ($discount->months) {
+ if ( $discount->months and $self->months > 0 ) {
# calculate months remaining on this cust_pkg_discount after this invoice
my $date = $self->cust_bill_pkg->cust_bill->_date;
my $used = FS::Record->scalar_sql(
@@ -152,7 +178,7 @@ sub description {
$used ||= 0;
my $remaining = sprintf('%.2f', $discount->months - $used);
$desc .= $self->mt(' for [quant,_1,month] ([quant,_2,month] remaining)',
- $self->months,
+ sprintf('%.2f', $self->months),
$remaining
);
}
diff --git a/FS/FS/cust_main/Packages.pm b/FS/FS/cust_main/Packages.pm
index c147e55..ead97f2 100644
--- a/FS/FS/cust_main/Packages.pm
+++ b/FS/FS/cust_main/Packages.pm
@@ -197,7 +197,7 @@ sub order_pkg {
map { $_ => $cust_pkg->$_() }
qw( pkgbatch
start_date order_date expire adjourn contract_end
- refnum discountnum waive_setup
+ refnum setup_discountnum recur_discountnum waive_setup
)
});
$error = $self->order_pkg('cust_pkg' => $pkg,
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 0ef7aa0..2772702 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -426,7 +426,7 @@ sub insert {
}
}
- if ( $self->discountnum ) {
+ if ( $self->setup_discountnum || $self->recur_discountnum ) {
my $error = $self->insert_discount();
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
@@ -4329,13 +4329,10 @@ sub insert_reason {
Associates this package with a discount (see L<FS::cust_pkg_discount>, possibly
inserting a new discount on the fly (see L<FS::discount>).
-Available options are:
-
-=over 4
-
-=item discountnum
-
-=back
+This will look at the cust_pkg for a pseudo-field named "setup_discountnum",
+and if present, will create a setup discount. If the discountnum is -1,
+a new discount definition will be inserted using the value in
+"setup_discountnum_amount" or "setup_discountnum_percent". Likewise for recur.
If there is an error, returns the error, otherwise returns false.
@@ -4345,21 +4342,29 @@ sub insert_discount {
#my ($self, %options) = @_;
my $self = shift;
- my $cust_pkg_discount = new FS::cust_pkg_discount {
- 'pkgnum' => $self->pkgnum,
- 'discountnum' => $self->discountnum,
- 'months_used' => 0,
- 'end_date' => '', #XXX
- #for the create a new discount case
- '_type' => $self->discountnum__type,
- 'amount' => $self->discountnum_amount,
- 'percent' => $self->discountnum_percent,
- 'months' => $self->discountnum_months,
- 'setup' => $self->discountnum_setup,
- #'disabled' => $self->discountnum_disabled,
- };
+ foreach my $x (qw(setup recur)) {
+ if ( my $discountnum = $self->get("${x}_discountnum") ) {
+ my $cust_pkg_discount = FS::cust_pkg_discount->new( {
+ 'pkgnum' => $self->pkgnum,
+ 'discountnum' => $discountnum,
+ 'setuprecur' => $x,
+ 'months_used' => 0,
+ 'end_date' => '', #XXX
+ #for the create a new discount case
+ 'amount' => $self->get("${x}_discountnum_amount"),
+ 'percent' => $self->get("${x}_discountnum_percent"),
+ 'months' => $self->get("${x}_discountnum_months"),
+ } );
+ if ( $x eq 'setup' ) {
+ $cust_pkg_discount->setup('Y');
+ $cust_pkg_discount->months('');
+ }
+ my $error = $cust_pkg_discount->insert;
+ return $error if $error;
+ }
+ }
- $cust_pkg_discount->insert;
+ '';
}
=item set_usage USAGE_VALUE_HASHREF
diff --git a/FS/FS/cust_pkg_discount.pm b/FS/FS/cust_pkg_discount.pm
index 5d0f85b..aa89816 100644
--- a/FS/FS/cust_pkg_discount.pm
+++ b/FS/FS/cust_pkg_discount.pm
@@ -59,6 +59,9 @@ end_date
order taker, see L<FS::access_user>
+=item setuprecur
+
+whether this discount applies to setup fees or recurring fees
=back
@@ -125,11 +128,29 @@ sub check {
|| $self->ut_alphan('otaker')
|| $self->ut_numbern('usernum')
|| $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
;
return $error if $error;
- return "Discount does not apply to setup fees, and package has no recurring"
- if ! $self->discount->setup && $self->cust_pkg->part_pkg->freq =~ /^0/;
+ my $cust_pkg = $self->cust_pkg;
+ my $discount = $self->discount;
+ if ( $self->setuprecur eq 'setup' ) {
+ if ( !$discount->setup ) {
+ # UI prevents this, and historical discounts should never have it either
+ return "Discount #".$self->discountnum." can't be applied to setup fees.";
+ } elsif ( $cust_pkg->base_setup == 0 ) {
+ # and this
+ return "Can't apply setup discount to a package with no setup fee.";
+ }
+ # else we're good. do NOT disallow applying setup discounts when the
+ # setup date is already set; upgrades use that.
+ } else {
+ if ( $self->cust_pkg->base_recur == 0 ) {
+ return "Can't apply recur discount to a package with no recurring fee.";
+ } elsif ( $cust_pkg->part_pkg->freq eq '0' ) {
+ return "Can't apply recur discount to a one-time charge.";
+ }
+ }
$self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
@@ -205,6 +226,45 @@ sub status {
sub _upgrade_data { # class method
my ($class, %opts) = @_;
$class->_upgrade_otaker(%opts);
+
+ # #14092: set setuprecur field on discounts. if we get one that applies to
+ # both setup and recur, split it into two discounts.
+ my $search = FS::Cursor->new({
+ table => 'cust_pkg_discount',
+ hashref => { setuprecur => '' }
+ });
+ while ( my $cust_pkg_discount = $search->fetch ) {
+ my $discount = $cust_pkg_discount->discount;
+ my $cust_pkg = $cust_pkg_discount->cust_pkg;
+ # 1. Does it apply to the setup fee?
+ # Yes, if: the discount applies to setup fees generally, and the package
+ # has a setup fee.
+ # No, if: the discount is a flat amount, and is not first-month only.
+ if ( $discount->setup
+ and $cust_pkg->base_setup > 0
+ and ($discount->amount == 0 or $discount->months == 1)
+ )
+ {
+ # then clone this discount into a new one
+ my $setup_discount = FS::cust_pkg_discount->new({
+ $cust_pkg_discount->hash,
+ setuprecur => 'setup',
+ pkgdiscountnum => ''
+ });
+ my $error = $setup_discount->insert;
+ die "$error (migrating cust_pkg_discount to setup discount)" if $error;
+ }
+ # 2. Does it apply to the recur fee?
+ # Yes, if: the package has a recur fee.
+ if ( $cust_pkg->base_recur > 0 ) {
+ # then modify this discount in place
+ $cust_pkg_discount->set('setuprecur' => 'recur');
+ my $error = $cust_pkg_discount->replace;
+ die "$error (migrating cust_pkg_discount)" if $error;
+ }
+ # not in here yet: splitting the cust_bill_pkg_discount records.
+ # (not really necessary)
+ }
}
=back
diff --git a/FS/FS/discount.pm b/FS/FS/discount.pm
index e113357..13146a9 100644
--- a/FS/FS/discount.pm
+++ b/FS/FS/discount.pm
@@ -119,12 +119,12 @@ sub check {
if ( $self->_type eq 'Select discount type' ) {
return 'Please select a discount type';
- } elsif ( $self->_type eq 'Amount' ) {
- $self->percent('0');
- return 'Amount must be greater than 0' unless $self->amount > 0;
- } elsif ( $self->_type eq 'Percentage' ) {
- $self->amount('0.00');
- return 'Percentage must be greater than 0' unless $self->percent > 0;
+ } elsif ( $self->amount > 0 ) {
+ $self->set('percent', '0');
+ } elsif ( $self->percent > 0 ) {
+ $self->set('amount', '0.00');
+ } else {
+ return "Discount amount or percentage must be > 0";
}
my $error =
diff --git a/FS/FS/part_pkg/discount_Mixin.pm b/FS/FS/part_pkg/discount_Mixin.pm
index 5de7d8e..1e39f6a 100644
--- a/FS/FS/part_pkg/discount_Mixin.pm
+++ b/FS/FS/part_pkg/discount_Mixin.pm
@@ -50,6 +50,9 @@ sub calc_discount {
my $tot_discount = 0;
#UI enforces just 1 for now, will need ordering when they can be stacked
+ # discount setup/recur splitting DOES NOT TOUCH THIS YET.
+ # we need some kind of monitoring to see who if anyone still uses term
+ # discounts.
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.
@@ -80,6 +83,13 @@ sub calc_discount {
}
my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
+
+ if ( defined $param->{'setup_charge'} ) {
+ @cust_pkg_discount = grep { $_->setuprecur eq 'setup' } @cust_pkg_discount;
+ } else {
+ @cust_pkg_discount = grep { $_->setuprecur eq 'recur' } @cust_pkg_discount;
+ }
+
foreach my $cust_pkg_discount ( @cust_pkg_discount ) {
my $discount_left;
my $discount = $cust_pkg_discount->discount;
@@ -115,23 +125,17 @@ sub calc_discount {
# if it's a flat amount discount for other than one month:
# - skip the discount. unsure, leaving it alone for now.
- next unless $discount->setup;
-
$months = 0; # never count a setup discount as a month of discount
# (the recur discount in the same month should do it)
if ( $discount->percent > 0 ) {
$amount = $discount->percent * $param->{'setup_charge'} / 100;
- } elsif ( $discount->amount > 0 && ($discount->months || 0) == 1) {
+ } elsif ( $discount->amount > 0 ) {
# apply the discount amount, up to a maximum of the setup charge
$amount = min($discount->amount, $param->{'setup_charge'});
$discount_left = sprintf('%.2f', $discount->amount - $amount);
# transfer remainder of discount, if any, to recur
$param->{'discount_left_recur'}{$discount->discountnum} = $discount_left;
- } else {
- # I guess we don't allow multiple-month flat amount discounts to
- # apply to setup?
- next;
}
} else {
@@ -180,20 +184,27 @@ sub calc_discount {
# recur discount is zero.
#}
- # transfer remainder of discount, if any, to setup
- # this is used when the recur phase wants to add a setup fee
+ # Transfer remainder of discount, if any, to setup
+ # This is used when the recur phase wants to add a setup fee
# (prorate_defer_bill): the "discount_left_setup" amount will
- # be subtracted in _make_lines.
- if ( $discount->setup && $discount->amount > 0
- && ($discount->months || 0) != 1
- )
+ # be subtracted in _make_lines.
+ if ( $discount->amount > 0 && ($discount->months || 0) != 1 )
{
- # $amount is no longer permonth at this point! correct. very good.
- $discount_left = $amount - $recur_charge; # backward, as above
- if ( $discount_left > 0 ) {
- $amount = $recur_charge;
- $param->{'discount_left_setup'}{$discount->discountnum} =
- 0 - $discount_left;
+ # make sure there is a setup discount with this discountnum
+ # on the same package.
+ if ( qsearchs('cust_pkg_discount', {
+ pkgnum => $cust_pkg->pkgnum,
+ discountnum => $discount->discountnum,
+ setuprecur => 'setup'
+ }) )
+ {
+ # $amount is no longer permonth at this point! correct. very good.
+ $discount_left = $amount - $recur_charge; # backward, as above
+ if ( $discount_left > 0 ) {
+ $amount = $recur_charge;
+ $param->{'discount_left_setup'}{$discount->discountnum} =
+ 0 - $discount_left;
+ }
}
}
@@ -210,7 +221,7 @@ sub calc_discount {
};
}
- }
+ } # else not 'setup_charge'
$amount = sprintf('%.2f', $amount + 0.00000001 ); #so 1.005 rounds to 1.01
@@ -221,7 +232,7 @@ sub calc_discount {
'pkgdiscountnum' => $cust_pkg_discount->pkgdiscountnum,
'amount' => $amount,
'months' => $months,
- # XXX should have a 'setuprecur'
+ # 'setuprecur' is implied by the cust_pkg_discount link
};
push @{ $param->{'discounts'} }, $cust_bill_pkg_discount;
$tot_discount += $amount;
diff --git a/httemplate/edit/cust_pkg_discount.html b/httemplate/edit/cust_pkg_discount.html
index 0bb84b8..e1e3dae 100755
--- a/httemplate/edit/cust_pkg_discount.html
+++ b/httemplate/edit/cust_pkg_discount.html
@@ -1,18 +1,5 @@
-<% include('/elements/header-popup.html', "Discount Package") %>
-
-<SCRIPT TYPE="text/javascript">
-
- function enable_discount_pkg () {
- if ( document.DiscountPkgForm.discountnum.selectedIndex > 0 ) {
- document.DiscountPkgForm.submit.disabled = false;
- } else {
- document.DiscountPkgForm.submit.disabled = true;
- }
- }
-
-</SCRIPT>
-
-<% include('/elements/error.html') %>
+<& /elements/header-popup.html, "Discount Package" &>
+<& /elements/error.html &>
<FORM NAME="DiscountPkgForm" ACTION="<% $p %>edit/process/cust_pkg_discount.html" METHOD=POST>
<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
@@ -26,17 +13,17 @@
</TD>
</TR>
-<% include('/elements/tr-select-discount.html',
- 'empty_label' => ( $pkgdiscountnum ? '' : 'Select discount' ),
- 'onchange' => 'enable_discount_pkg()',
- 'cgi' => $cgi,
- )
-%>
-
+<& /elements/tr-select-pkg-discount.html,
+ curr_value_setup => $setup_discountnum,
+ curr_value_recur => $recur_discountnum,
+ disable_setup => $disable_setup,
+ disable_recur => $disable_recur,
+&>
+
</TABLE>
<BR>
-<INPUT NAME="submit" TYPE="submit" VALUE="Discount package" <% $pkgdiscountnum ? '' : 'DISABLED' %>>
+<INPUT NAME="submit" TYPE="submit" VALUE="Discount package">
</FORM>
</BODY>
@@ -44,14 +31,13 @@
<%init>
-#some false laziness w/misc/change_pkg.cgi
-
my $conf = new FS::Conf;
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('Discount customer package');
+ unless $curuser->access_right([ 'Discount customer package',
+ 'Waive setup fee']);
my $pkgnum = scalar($cgi->param('pkgnum'));
$pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum";
@@ -67,10 +53,30 @@ my $cust_pkg =
'extra_sql' => ' AND '. $curuser->agentnums_sql,
}) or die "unknown pkgnum $pkgnum";
-#my $cust_main = $cust_pkg->cust_main
-# or die "can't get cust_main record for custnum ". $cust_pkg->custnum.
-# " ( pkgnum ". cust_pkg->pkgnum. ")";
-
my $part_pkg = $cust_pkg->part_pkg;
+my @discounts = $cust_pkg->cust_pkg_discount_active;
+my ($setup_discountnum, $recur_discountnum);
+foreach (@discounts) {
+ if ( $_->setuprecur eq 'setup') {
+ die "multiple setup discounts on pkg#$pkgnum" if $setup_discountnum;
+ $setup_discountnum = $_->discountnum;
+ } elsif ( $_->setuprecur eq 'recur' ) {
+ die "multiple setup discounts on pkg#$pkgnum" if $recur_discountnum;
+ $recur_discountnum = $_->discountnum;
+ }
+}
+if ( $cust_pkg->waive_setup ) {
+ $setup_discountnum = -2;
+}
+
+my $disable_setup = 1;
+if ( !$cust_pkg->get('setup') and $cust_pkg->base_setup > 0 ) {
+ $disable_setup = 0;
+}
+my $disable_recur = 1;
+if ( $cust_pkg->base_recur > 0 ) {
+ $disable_recur = 0;
+}
+
</%init>
diff --git a/httemplate/edit/process/cust_pkg_discount.html b/httemplate/edit/process/cust_pkg_discount.html
index 4a71f69..143611e 100644
--- a/httemplate/edit/process/cust_pkg_discount.html
+++ b/httemplate/edit/process/cust_pkg_discount.html
@@ -14,9 +14,8 @@
<%init>
my $curuser = $FS::CurrentUser::CurrentUser;
-
-die "access denied"
- unless $curuser->access_right('Discount customer package');
+my $can_discount = $curuser->access_right('Discount customer package');
+my $can_waive_setup = $curuser->access_right('Waive setup fee');
#this search is really for security wrt agent virt...
#maybe move it to the cust_pkg_discount->insert call?
@@ -29,20 +28,81 @@ my $cust_pkg = qsearchs({
});
die 'unknown pkgnum' unless $cust_pkg;
-my $cust_pkg_discount = new FS::cust_pkg_discount {
- 'pkgnum' => $cust_pkg->pkgnum,
- 'discountnum' => scalar($cgi->param('discountnum')),
- 'months_used' => 0,
- 'end_date' => '', #XXX
- #for the create a new discount case
- '_type' => scalar($cgi->param('discountnum__type')),
- 'amount' => scalar($cgi->param('discountnum_amount')),
- 'percent' => scalar($cgi->param('discountnum_percent')),
- 'months' => scalar($cgi->param('discountnum_months')),
- 'setup' => scalar($cgi->param('discountnum_setup')),
- #'linked' => scalar($cgi->param('discountnum_linked')),
- #'disabled' => $self->discountnum_disabled,
-};
-my $error = $cust_pkg_discount->insert;
+my $error;
+my %discountnum = (setup => '', recur => '');
+if ( $cgi->param('setup_discountnum') == -2 ) {
+
+ die "access denied" unless $can_waive_setup; # UI protects against this
+ # waive setup fee (not really a discount but treated as one in the UI)
+ if ( !$cust_pkg->get('setup') and !$cust_pkg->waive_setup ) {
+ $cust_pkg->set('waive_setup' => 'Y');
+ $error = $cust_pkg->replace;
+ }
+
+} else {
+ if ( $cgi->param('setup_discountnum') =~ /^(-?\d+)$/ ) {
+ $discountnum{setup} = $1;
+ }
+ if ( $cust_pkg->waive_setup ) {
+ $cust_pkg->set('waive_setup', '');
+ $error = $cust_pkg->replace;
+ }
+}
+
+if ( $cgi->param('recur_discountnum') =~ /^(-?\d+)$/ ) {
+
+ $discountnum{recur} = $1;
+
+}
+
+my @active_discounts = $cust_pkg->cust_pkg_discount_active;
+
+foreach my $setuprecur (qw(setup recur)) {
+
+ if ( $cust_pkg->get('setup') and $setuprecur eq 'setup' ) {
+ # no point allowing setup discounts to be edited for a previously setup
+ # package
+ next;
+ }
+
+ my ($active) = grep { $_->setuprecur eq $setuprecur } @active_discounts;
+
+ if ( $active ) {
+ if ( $active->discount ne $discountnum{$setuprecur} ) {
+ $active->set('disabled' => 'Y');
+ $error ||= $active->replace;
+ undef $active;
+ } else {
+ # it's the same discountnum; don't touch it
+ next;
+ }
+ }
+
+ if ( $discountnum{$setuprecur} ) {
+ die "access_denied" unless $can_discount;
+ my $cust_pkg_discount = FS::cust_pkg_discount->new({
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'discountnum' => $discountnum{$setuprecur},
+ 'setuprecur' => $setuprecur,
+ 'months_used' => 0,
+ 'end_date' => '', #XXX
+ #for the create a new discount case
+ '_type' => scalar($cgi->param($setuprecur.'_discountnum__type')),
+ 'amount' => scalar($cgi->param($setuprecur.'_discountnum_amount')),
+ 'percent' => scalar($cgi->param($setuprecur.'_discountnum_percent')),
+ });
+ if ( $setuprecur eq 'setup' ) {
+ $cust_pkg_discount->set('setup' => 'Y');
+ $cust_pkg_discount->set('months' => 1);
+ } else {
+ if ( $cgi->param($setuprecur.'_discountnum_months') =~ /^(\w+)$/ ) {
+ $cust_pkg_discount->set('months' => $1);
+ }
+ }
+
+ $error ||= $cust_pkg_discount->insert;
+
+ }
+} # foreach $setuprecur
</%init>
diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi
index f1d8c26..6035215 100644
--- a/httemplate/edit/process/quick-cust_pkg.cgi
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@ -79,9 +79,6 @@ my $contactnum = $1;
$cgi->param('locationnum') =~ /^(\-?\d*)$/
or die 'illegal locationnum '. $cgi->param('locationnum');
my $locationnum = $1;
-$cgi->param('discountnum') =~ /^(\-?\d*)$/
- or die 'illegal discountnum '. $cgi->param('discountnum');
-my $discountnum = $1;
# for going right to a provision service after ordering a package
my( $svcpart, $part_svc ) = ( '', '' );
@@ -114,19 +111,29 @@ my %hash = (
'refnum' => $refnum,
'contactnum' => $contactnum,
'locationnum' => $locationnum,
- 'discountnum' => $discountnum,
- #for the create a new discount case
- 'discountnum__type' => scalar($cgi->param('discountnum__type')),
- 'discountnum_amount' => scalar($cgi->param('discountnum_amount')),
- 'discountnum_percent' => scalar($cgi->param('discountnum_percent')),
- 'discountnum_months' => scalar($cgi->param('discountnum_months')),
- 'discountnum_setup' => scalar($cgi->param('discountnum_setup')),
'contract_end' => ( scalar($cgi->param('contract_end'))
? parse_datetime($cgi->param('contract_end'))
: ''
),
- 'waive_setup' => ( $cgi->param('waive_setup') eq 'Y' ? 'Y' : '' ),
);
+
+if ( $cgi->param('setup_discountnum') =~ /^(-?\d+)$/ ) {
+ if ( $1 == -2 ) {
+ $hash{waive_setup} = 'Y';
+ } else {
+ $hash{setup_discountnum} = $1;
+ $hash{setup_discountnum_amount} = $cgi->param('setup_discountnum_amount');
+ $hash{setup_discountnum_percent} = $cgi->param('setup_discountnum_percent');
+ }
+}
+
+if ( $cgi->param('recur_discountnum') =~ /^(-?\d+)$/ ) {
+ $hash{recur_discountnum} = $1;
+ $hash{recur_discountnum_amount} = $cgi->param('recur_discountnum_amount');
+ $hash{recur_discountnum_percent} = $cgi->param('recur_discountnum_percent');
+ $hash{recur_discountnum_months} = $cgi->param('recur_discountnum_months');
+}
+
$hash{'custnum'} = $cust_main->custnum if $cust_main;
if ( $cgi->param('start') eq 'on_hold' ) {
diff --git a/httemplate/elements/tr-select-months.html b/httemplate/elements/select-months.html
similarity index 90%
copy from httemplate/elements/tr-select-months.html
copy to httemplate/elements/select-months.html
index b90ce1e..1cd72fc 100644
--- a/httemplate/elements/tr-select-months.html
+++ b/httemplate/elements/select-months.html
@@ -8,4 +8,4 @@ $opt{labels} = { '' => '',
};
</%init>
-<& tr-select.html, %opt &>
+<& select.html, %opt &>
diff --git a/httemplate/elements/tr-select-months.html b/httemplate/elements/tr-select-months.html
index b90ce1e..4d85764 100644
--- a/httemplate/elements/tr-select-months.html
+++ b/httemplate/elements/tr-select-months.html
@@ -1,11 +1,5 @@
-<%init>
-my %opt = @_;
-$opt{id} ||= $opt{field}; # should be the default everywhere
-my $max = $opt{max} || 36;
-$opt{options} = [ '', 1 .. $max ];
-$opt{labels} = { '' => '',
- map { $_ => emt('[quant,_1,month]', $_) } 1 .. $max
- };
-
-</%init>
-<& tr-select.html, %opt &>
+<& tr-td-label.html, @_ &>
+ <td>
+ <& select-months.html, @_ &>
+ </td>
+</tr>
diff --git a/httemplate/elements/tr-select-pkg-discount.html b/httemplate/elements/tr-select-pkg-discount.html
new file mode 100644
index 0000000..dc38cff
--- /dev/null
+++ b/httemplate/elements/tr-select-pkg-discount.html
@@ -0,0 +1,196 @@
+<%doc>
+
+In order_pkg.html or similar:
+
+<& /elements/tr-select-pkg-discount.html,
+ curr_value_setup => ($cgi->param('setup_discountnum') || ''),
+ curr_value_recur => ($cgi->param('recur_discountnum') || ''),
+ disable_setup => 0,
+ disable_recur => 0,
+&>
+
+This provides the following:
+- If the user can waive setup fees or apply a discount, they get a
+ select box for the setup discount, with "Waive setup fee" as an option.
+- If they can custom discount, they will also get "Custom discount" as an
+ option. If selected, this will show fields to enter the custom discount
+ amount/percentage.
+- If they can waive setup fees but NOT apply a discount, they only get a
+ checkbox to waive setup fee.
+- Same for recurring fee, but without the "waive setup fee" stuff, obviously.
+- Custom recurring discounts also have an option for a duration in months.
+
+"disable_setup" locks the setup discount, but will still show a static
+description if curr_value_setup is set. Likewise "disable_recur".
+
+</%doc>
+% # SETUP DISCOUNT
+
+% # select-discount knows about the "custom discount" ACL
+% if ( $curuser->access_right('Discount customer package')
+% and !$opt{disable_setup} )
+% {
+% my $pre_options = [ '' => '(none)' ];
+% if ( $curuser->access_right('Waive setup fee') ) {
+% push @$pre_options, -2 => 'Waive setup fee';
+% }
+<& tr-td-label.html, label => emt('Setup fee') &>
+ <td>
+ <& select-discount.html,
+ field => 'setup_discountnum',
+ id => 'setup_discountnum',
+ hashref => { disabled => '',
+ setup => 'Y'
+ },
+ extra_sql => ' AND (percent > 0 OR months = 1)',
+ curr_value => $opt{'curr_value_setup'},
+ disable_empty => 1,
+ pre_options => $pre_options,
+ &>
+ </td>
+</tr>
+% # custom discount
+<tr class="setup_discount_custom">
+ <td></td>
+ <td>Amount <% $money_char %>
+ <& input-text.html,
+ field => 'setup_discountnum_amount',
+ curr_value => ($cgi->param('setup_discountnum_amount') || ''),
+ size => 5,
+ &>
+ or percentage
+ <& input-text.html,
+ field => 'setup_discountnum_percent',
+ curr_value => ($cgi->param('setup_discountnum_percent') || ''),
+ size => 5,
+ &> %
+ </td>
+</tr>
+
+% } elsif ( $curuser->access_right('Waive setup fee')
+% and !$opt{disable_setup} )
+% {
+
+<& tr-td-label.html, label => emt('Waive setup fee') &>
+ <td>
+ <& checkbox.html,
+ field => 'setup_discountnum',
+ id => 'setup_discountnum',
+ value => '-2',
+ curr_value => $opt{'curr_value_setup'},
+ &>
+ </td>
+</tr>
+
+% } elsif ( $opt{'curr_value_setup'} ) { # user can't do anything
+%
+% my $discount = FS::discount->by_key($opt{'curr_value_setup'});
+
+ <INPUT TYPE="hidden" NAME="setup_discountnum" VALUE="<% $opt{curr_value_setup} %>">
+
+ <% $discount->description_short %>
+
+% }
+
+% # RECUR DISCOUNT
+
+% if ( $curuser->access_right('Discount customer package')
+% and !$opt{disable_recur} ) {
+
+<& tr-td-label.html, label => emt('Recurring fee') &>
+ <td>
+ <& select-discount.html,
+ field => 'recur_discountnum',
+ id => 'recur_discountnum',
+ hashref => { disabled => '' },
+ curr_value => $opt{'curr_value_recur'},
+ &>
+
+ </td>
+</tr>
+% # custom discount
+<tr class="recur_discount_custom">
+ <td></td>
+ <td>Amount <% $money_char %>
+ <& input-text.html,
+ field => 'recur_discountnum_amount',
+ curr_value => ($cgi->param('recur_discountnum_amount') || ''),
+ size => 5,
+ &>
+ or percentage
+ <& input-text.html,
+ field => 'recur_discountnum_percent',
+ curr_value => ($cgi->param('recur_discountnum_percent') || ''),
+ size => 5,
+ &> %
+ </td>
+</tr>
+<tr class="recur_discount_custom">
+ <td></td>
+ <td>Expires after
+ <& /elements/select-months.html,
+ field => 'recur_discountnum_months',
+ curr_value => ($cgi->param('recur_discountnum_months') || ''),
+ &>
+ </td>
+</tr>
+
+% } elsif ( $opt{'curr_value_recur'} ) {
+%
+% my $discount = FS::discount->by_key($opt{'curr_value_recur'});
+
+ <INPUT TYPE="hidden" NAME="recur_discountnum" VALUE="<% $opt{curr_value_recur} %>">
+
+ <% $discount->description %>
+
+% }
+
+<SCRIPT TYPE="text/javascript">
+$(document).ready(function() {
+ ['setup', 'recur'].forEach(function(x) {
+ var discountnum = $('#'+x+'_discountnum');
+
+ // if it's been set to a custom discount, show custom discount inputs
+ var discountnum_changed = function() {
+ var val = this.value;
+ var custom = $('.'+x+'_discount_custom');
+ if ( val == -1 ) {
+ custom.show();
+ } else {
+ custom.hide();
+ }
+ };
+
+ discountnum.on('change', discountnum_changed);
+ discountnum.trigger('change');
+
+ // if amount contains a value, disable percent, and vice versa
+ var amount_percent_keyup = function(event) {
+ var other = event.data;
+ if (this.value.length > 0) {
+ other.disabled = true;
+ } else {
+ other.disabled = false;
+ }
+ };
+ var amount = $('#'+x+'_discountnum_amount');
+ var percent = $('#'+x+'_discountnum_percent');
+ amount.on('keyup', percent, amount_percent_keyup);
+ percent.on('keyup', amount, amount_percent_keyup);
+
+ amount.trigger('keyup');
+ percent.trigger('keyup');
+ });
+});
+</script>
+<%init>
+
+my %opt = (
+ 'curr_value_setup' => ($cgi->param('setup_discountnum') || ''),
+ 'curr_value_recur' => ($cgi->param('recur_discountnum') || ''),
+ @_
+);
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index cb2bd48..e282501 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -127,22 +127,7 @@
% if ( $discount_cust_pkg || $waive_setup_fee ) {
<FONT CLASS="fsinnerbox-title"><% mt('Discounting') |h %></FONT>
<% ntable("#cccccc") %>
-
-% if ( $waive_setup_fee ) {
- <TR>
- <TH ALIGN="right"><% mt('Waive setup fee') |h %> </TH>
- <TD COLSPAN=6><INPUT TYPE="checkbox" NAME="waive_setup" VALUE="Y"></TD>
- </TR>
-% }
-
-% if ( $discount_cust_pkg ) {
- <& /elements/tr-select-discount.html,
- 'element_etc' => 'DISABLED',
- 'colspan' => 7,
- 'cgi' => $cgi,
- &>
-% }
-
+ <& /elements/tr-select-pkg-discount.html &>
</TABLE><BR>
% }
diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html
index 6da5787..c634828 100644
--- a/httemplate/search/cust_bill_pkg_discount.html
+++ b/httemplate/search/cust_bill_pkg_discount.html
@@ -20,9 +20,7 @@
sub { $_[0]->cust_pkg_discount->discount->description },
sub { $_[0]->cust_pkg_discount->discount->classname },
sub { sprintf($money_char.'%.2f', shift->amount ) },
- sub { my $m = shift->months;
- $m =~ /\./ ? sprintf('%.2f', $m) : $m;
- },
+ $months_sub,
'pkg',#sub { $_[0]->cust_bill_pkg->cust_pkg->part_pkg->pkg },
'invnum',
sub { time2str('%b %d %Y', shift->_date ) },
@@ -218,4 +216,11 @@ my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
+my $months_sub = sub {
+ my $cust_bill_pkg_discount = shift;
+ return 'Setup'
+ if $cust_bill_pkg_discount->cust_pkg_discount->setuprecur eq 'setup';
+ sprintf('%.2f', $cust_bill_pkg_discount->months);
+};
+
</%init>
diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html
index f0c7447..ab6ad2b 100644
--- a/httemplate/search/cust_pkg_discount.html
+++ b/httemplate/search/cust_pkg_discount.html
@@ -18,9 +18,7 @@
sub { ucfirst( shift->status ) },
sub { shift->discount->description },
sub { shift->discount->classname },
- sub { my $m = shift->months_used;
- $m =~ /\./ ? sprintf('%.2f',$m) : $m;
- },
+ $months_used_sub,
'otaker',
'pkg',
\&FS::UI::Web::cust_fields,
@@ -165,4 +163,9 @@ my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
my $conf = new FS::Conf;
+my $months_used_sub = sub {
+ my $cust_pkg_discount = shift;
+ return 'Setup only' if $cust_pkg_discount->setuprecur eq 'setup';
+ return sprintf('%.2f', $cust_pkg_discount->months_used);
+};
</%init>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
index 4903e18..0c67843 100755
--- a/httemplate/view/cust_main/packages.html
+++ b/httemplate/view/cust_main/packages.html
@@ -42,6 +42,7 @@ table.hiddenrows {
z-index: 1;
text-align: center;
}
+
</STYLE>
% # activate rolldown buttons for hidden package blocks
<SCRIPT TYPE="text/javascript">
diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html
index 8aa6403..e98b95e 100644
--- a/httemplate/view/cust_main/packages/package.html
+++ b/httemplate/view/cust_main/packages/package.html
@@ -53,10 +53,7 @@
( <%onetime_change_link($cust_pkg)%> )
% }
% # also, you can discount it
-% if ( $curuser->access_right('Discount customer package')
-% && ! scalar($cust_pkg->cust_pkg_discount_active)
-% && ! scalar($cust_pkg->part_pkg->part_pkg_discount)
-% ) {
+% if ( $can_discount_pkg ) {
( <%pkg_discount_link($cust_pkg)%> )
% }
<BR>
@@ -79,14 +76,7 @@
( <%pkg_change_link($cust_pkg)%> )
% }
%
-% if ( $curuser->access_right('Discount customer package')
-% && $part_pkg->can_discount
-% && ! scalar( @{ $cust_pkg->{_cust_pkg_discount_active} } )
-% && ( ! $opt{'term_discounts'}
-% || ! scalar($cust_pkg->part_pkg->part_pkg_discount)
-% )
-% )
-% {
+% if ( $can_discount_pkg ) {
% $br=1;
( <%pkg_discount_link($cust_pkg)%> )
% }
@@ -437,4 +427,21 @@ sub pkg_event_link {
'</a>';
}
+# figure out if this user will be able to edit either the setup or recurring
+# discounts for this package
+my $can_discount_pkg = (
+ $part_pkg->can_discount
+ and
+ ( ( $curuser->access_right(['Discount customer package', 'Waive setup fee'])
+ and $cust_pkg->base_setup > 0
+ and !$cust_pkg->setup
+ )
+ or
+ ( $curuser->access_right('Discount customer package')
+ and $cust_pkg->base_recur > 0
+ and $cust_pkg->freq ne '0'
+ )
+ )
+);
+
</%init>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 7e125f7..13bd202 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -461,10 +461,11 @@ sub pkg_status_row_changed {
my $part_pkg = $old->part_pkg;
$html .= pkg_status_row_colspan(
$cust_pkg,
- emt("Changed from [_1]: [_2]",
- $cust_pkg->change_pkgnum,
- $part_pkg->pkg_comment(cust_pkg=>$old, nopartpkg=>1)
- ),
+# emt("Changed from [_1]: [_2]",
+# $cust_pkg->change_pkgnum,
+# $part_pkg->pkg_comment(cust_pkg=>$old, nopartpkg=>1)
+# ),
+ '',
'',
'size' => '-1',
'align' => 'right',
@@ -529,17 +530,25 @@ sub pkg_status_row_discount {
my $discount = $cust_pkg_discount->discount;
- my $label = '<B>'.emt('Discount').'</B>: '. $discount->description;
- if ( $discount->months ) {
+ my $label = '<SPAN STYLE="font-size: small"><B>';
+ if ( $cust_pkg_discount->setuprecur eq 'setup' ) {
+ $label .= emt('Setup Discount');
+ } else {
+ $label .= emt('Recurring Discount');
+ }
+ $label .= '</B>: '. $discount->description;
+ warn Dumper $cust_pkg_discount;
+ if ( $discount->months > 0 and $cust_pkg_discount->months_used > 0 ) {
my $remaining = $discount->months - $cust_pkg_discount->months_used;
$remaining = sprintf('%.2f', $remaining) if $remaining =~ /\./;
- $label .= emt("([_1] months remaining)",$remaining);
+ $label .= <br> . emt("([_1] months remaining)",$remaining);
}
+ $label .= '</SPAN>';
- $label .= ' <FONT SIZE="-1">('.
- '<A HREF="../misc/delete-cust_pkg_discount.html?'.
- $cust_pkg_discount->pkgdiscountnum.
- '">'.emt('remove discount').'</A>)</FONT>';
+ #$label .= ' <FONT SIZE="-1">('.
+ # '<A HREF="../misc/delete-cust_pkg_discount.html?'.
+ # $cust_pkg_discount->pkgdiscountnum.
+ # '">'.emt('remove discount').'</A>)</FONT>';
$html .= pkg_status_row_colspan( $cust_pkg, $label, '', %opt );
-----------------------------------------------------------------------
Summary of changes:
FS/FS/Schema.pm | 1 +
FS/FS/Template_Mixin.pm | 33 ++--
FS/FS/cust_bill_pkg.pm | 11 +-
FS/FS/cust_bill_pkg_discount.pm | 30 ++-
FS/FS/cust_main/Packages.pm | 2 +-
FS/FS/cust_pkg.pm | 49 ++---
FS/FS/cust_pkg_discount.pm | 64 ++++++-
FS/FS/discount.pm | 12 +-
FS/FS/part_pkg/discount_Mixin.pm | 53 +++---
httemplate/edit/cust_pkg_discount.html | 66 ++++---
httemplate/edit/process/cust_pkg_discount.html | 96 ++++++++--
httemplate/edit/process/quick-cust_pkg.cgi | 29 +--
.../{tr-select-months.html => select-months.html} | 2 +-
httemplate/elements/tr-select-months.html | 16 +-
httemplate/elements/tr-select-pkg-discount.html | 196 ++++++++++++++++++++
httemplate/misc/order_pkg.html | 17 +-
httemplate/search/cust_bill_pkg_discount.html | 11 +-
httemplate/search/cust_pkg_discount.html | 9 +-
httemplate/view/cust_main/packages.html | 1 +
httemplate/view/cust_main/packages/package.html | 31 ++--
httemplate/view/cust_main/packages/status.html | 31 ++--
21 files changed, 566 insertions(+), 194 deletions(-)
copy httemplate/elements/{tr-select-months.html => select-months.html} (90%)
create mode 100644 httemplate/elements/tr-select-pkg-discount.html
More information about the freeside-commits
mailing list