From f641486e28214ad1eca18c47d2252701b83614f1 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Fri, 16 Oct 2015 15:32:32 -0700 Subject: [PATCH] separate setup and recur discounts, #14092 --- 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 ++-- httemplate/elements/select-months.html | 11 ++ 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, 576 insertions(+), 193 deletions(-) create mode 100644 httemplate/elements/select-months.html create mode 100644 httemplate/elements/tr-select-pkg-discount.html diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 479ab1081..ceb347d8e 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -2906,6 +2906,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 1a3217c44..ffaef9707 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 178042666..5861ee47f 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 9e64d2076..616657a4f 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 c147e551c..ead97f2c3 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 279205b19..d7419078b 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -425,7 +425,7 @@ sub insert { } } - if ( $self->discountnum ) { + if ( $self->setup_discountnum || $self->recur_discountnum ) { my $error = $self->insert_discount(); if ( $error ) { $dbh->rollback if $oldAutoCommit; @@ -4318,13 +4318,10 @@ sub insert_reason { Associates this package with a discount (see L, possibly inserting a new discount on the fly (see L). -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. @@ -4334,21 +4331,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 5d0f85b5e..aa8981621 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 +=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 e11335741..13146a91b 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 5de7d8ea5..1e39f6aef 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 0bb84b8f2..e1e3daede 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") %> - - - -<% include('/elements/error.html') %> +<& /elements/header-popup.html, "Discount Package" &> +<& /elements/error.html &>
@@ -26,17 +13,17 @@ -<% 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, +&> +
-> +
@@ -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; +} + diff --git a/httemplate/edit/process/cust_pkg_discount.html b/httemplate/edit/process/cust_pkg_discount.html index 4a71f6975..143611ef9 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 diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index f1d8c2696..60352154a 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/select-months.html b/httemplate/elements/select-months.html new file mode 100644 index 000000000..1cd72fcba --- /dev/null +++ b/httemplate/elements/select-months.html @@ -0,0 +1,11 @@ +<%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 + }; + + +<& select.html, %opt &> diff --git a/httemplate/elements/tr-select-months.html b/httemplate/elements/tr-select-months.html index b90ce1ed7..4d8576411 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 - }; - - -<& tr-select.html, %opt &> +<& tr-td-label.html, @_ &> + + <& select-months.html, @_ &> + + diff --git a/httemplate/elements/tr-select-pkg-discount.html b/httemplate/elements/tr-select-pkg-discount.html new file mode 100644 index 000000000..dc38cff3e --- /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". + + +% # 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') &> + + <& 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, + &> + + +% # custom discount + + + 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, + &> % + + + +% } elsif ( $curuser->access_right('Waive setup fee') +% and !$opt{disable_setup} ) +% { + +<& tr-td-label.html, label => emt('Waive setup fee') &> + + <& checkbox.html, + field => 'setup_discountnum', + id => 'setup_discountnum', + value => '-2', + curr_value => $opt{'curr_value_setup'}, + &> + + + +% } elsif ( $opt{'curr_value_setup'} ) { # user can't do anything +% +% my $discount = FS::discount->by_key($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') &> + + <& select-discount.html, + field => 'recur_discountnum', + id => 'recur_discountnum', + hashref => { disabled => '' }, + curr_value => $opt{'curr_value_recur'}, + &> + + + +% # custom discount + + + 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, + &> % + + + + + Expires after + <& /elements/select-months.html, + field => 'recur_discountnum_months', + curr_value => ($cgi->param('recur_discountnum_months') || ''), + &> + + + +% } elsif ( $opt{'curr_value_recur'} ) { +% +% my $discount = FS::discount->by_key($opt{'curr_value_recur'}); + + + + <% $discount->description %> + +% } + + +<%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') || '$'; + + diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html index cb2bd4832..e28250110 100644 --- a/httemplate/misc/order_pkg.html +++ b/httemplate/misc/order_pkg.html @@ -127,22 +127,7 @@ % if ( $discount_cust_pkg || $waive_setup_fee ) { <% mt('Discounting') |h %> <% ntable("#cccccc") %> - -% if ( $waive_setup_fee ) { - - <% mt('Waive setup fee') |h %> - - -% } - -% if ( $discount_cust_pkg ) { - <& /elements/tr-select-discount.html, - 'element_etc' => 'DISABLED', - 'colspan' => 7, - 'cgi' => $cgi, - &> -% } - + <& /elements/tr-select-pkg-discount.html &>
% } diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html index 6da5787a0..c63482827 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); +}; + diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html index f0c7447a5..ab6ad2bd0 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); +}; diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 4903e185b..0c67843d4 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; } + % # activate rolldown buttons for hidden package blocks