From: Ivan Kohler Date: Wed, 28 Oct 2015 18:24:46 +0000 (-0700) Subject: Merge branch 'master' of git.freeside.biz:/home/git/freeside X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=3888fd6d1902f93c19d62df186e04617357f58c5;hp=6995be2fb5e8c9a07b38c0bb8976a10a9381fc90 Merge branch 'master' of git.freeside.biz:/home/git/freeside --- diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index 98b87ad55..89c70f75c 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -2424,7 +2424,7 @@ sub change_pkg { if ( $conf->exists('signup_server-realtime') ) { - my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_credit'=>1 ); + my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_invoice_void'=>1 ); if ($bill_error) { $err_or_cust_pkg->suspend; @@ -2500,8 +2500,12 @@ sub _do_bop_realtime { my $old_balance = $cust_main->balance; - my $bill_error = $cust_main->bill - || $cust_main->apply_payments_and_credits; + my @cust_bill; + my $bill_error = $cust_main->bill( + 'return_bill' => \@cust_bill, + ); + + $bill_error ||= $cust_main->apply_payments_and_credits; $bill_error ||= $cust_main->realtime_collect('selfservice' => 1) if $cust_main->payby =~ /^(CARD|CHEK)$/; @@ -2513,14 +2517,14 @@ sub _do_bop_realtime { ) ) { - unless ( $opt{'no_credit'} ) { - #this makes sense. credit is "un-doing" the invoice - my $conf = new FS::Conf; - $cust_main->credit( sprintf("%.2f", $cust_main->balance-$old_balance ), - 'self-service decline', - reason_type=>$conf->config('signup_credit_type'), - ); - $cust_main->apply_credits( 'order' => 'newest' ); + unless ( $opt{'no_invoice_void'} ) { + + #this used to apply a credit, but now we can void invoices... + foreach my $cust_bill (@cust_bill) { + my $voiderror = $cust_bill->void(); + warn "Error voiding cust bill after decline: $voiderror"; + } + } return { 'error' => '_decline', 'bill_error' => $bill_error }; diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm index c0a9d98ce..a4ea21b8f 100644 --- a/FS/FS/ClientAPI/Signup.pm +++ b/FS/FS/ClientAPI/Signup.pm @@ -783,7 +783,11 @@ sub new_customer { #warn "$me Billing customer...\n" if $Debug; - my $bill_error = $cust_main->bill( 'depend_jobnum'=>$placeholder->jobnum ); + my @cust_bill; + my $bill_error = $cust_main->bill( + 'depend_jobnum' => $placeholder->jobnum, + 'return_bill' => \@cust_bill, + ); #warn "$me error billing new customer: $bill_error" # if $bill_error; @@ -818,11 +822,11 @@ sub new_customer { if ( $cust_main->balance > 0 ) { - #this makes sense. credit is "un-doing" the invoice - $cust_main->credit( $cust_main->balance, 'signup server decline', - 'reason_type' => $conf->config('signup_credit_type'), - ); - $cust_main->apply_credits; + #this used to apply a credit, but now we can void invoices... + foreach my $cust_bill (@cust_bill) { + my $voiderror = $cust_bill->void(); + warn "Error voiding cust bill after decline: $voiderror"; + } #should check list for errors... #$cust_main->suspend; diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 594c0e076..990f2a3be 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -4176,9 +4176,10 @@ and customer address. Include units.', reason_type_options('R'), }, + # was only used to negate invoices during signup when card was declined, now we just void { 'key' => 'signup_credit_type', - 'section' => 'billing', #self-service? + 'section' => 'deprecated', #self-service? 'description' => 'The group to use for new, automatically generated credit reasons resulting from signup and self-service declines.', reason_type_options('R'), }, diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 7dc54f7c4..447302b36 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1985,8 +1985,8 @@ sub tables_hashref { 'quotationpkgdiscountnum', 'serial', '', '', '', '', 'quotationpkgnum', 'int', '', '', '', '', 'discountnum', 'int', '', '', '', '', - 'setup_amount', @money_typen, '', '', - 'recur_amount', @money_typen, '', '', + 'setuprecur', 'varchar', 'NULL', $char_d, '', '', + 'amount', @money_typen, '', '', #'end_date', @date_type, '', '', ], 'primary_key' => 'quotationpkgdiscountnum', diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm index 31adebec1..2f2338ee4 100644 --- a/FS/FS/cust_credit.pm +++ b/FS/FS/cust_credit.pm @@ -40,7 +40,6 @@ $FS::UID::callback{'FS::cust_credit'} = sub { our %reasontype_map = ( 'referral_credit_type' => 'Referral Credit', 'cancel_credit_type' => 'Cancellation Credit', - 'signup_credit_type' => 'Self-Service Credit', ); =head1 NAME diff --git a/FS/FS/part_pkg/discount_Mixin.pm b/FS/FS/part_pkg/discount_Mixin.pm index 1e39f6aef..dcca3436f 100644 --- a/FS/FS/part_pkg/discount_Mixin.pm +++ b/FS/FS/part_pkg/discount_Mixin.pm @@ -145,7 +145,7 @@ sub calc_discount { # XXX it would be more accurate for calc_recur to just _tell us_ what # it's going to charge - my $recur_charge = $br * ($cust_pkg->quantity || 1) * $chg_months / $self->freq; + my $recur_charge = $br * $chg_months / $self->freq; # round this, because the real recur charge is rounded $recur_charge = sprintf('%.2f', $recur_charge); diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm index d66b1b8e4..c4004934a 100644 --- a/FS/FS/quotation.pm +++ b/FS/FS/quotation.pm @@ -260,6 +260,7 @@ sub _items_sections { my %opt = @_; my $escape = $opt{escape}; # the only one we care about + my %show; # package frequency => 1 if there's anything to display my %subtotals = (); # package frequency => subtotal my $disable_total = 0; foreach my $pkg ($self->quotation_pkg) { @@ -267,6 +268,8 @@ sub _items_sections { my $part_pkg = $pkg->part_pkg; my $recur_freq = $part_pkg->freq; + $show{$recur_freq} = 1 if $pkg->unitrecur > 0; + $show{0} = 1 if $pkg->unitsetup > 0; ($subtotals{0} ||= 0) += $pkg->setup + $pkg->setup_tax; ($subtotals{$recur_freq} ||= 0) += $pkg->recur + $pkg->recur_tax; @@ -288,7 +291,8 @@ sub _items_sections { my $no_recurring = 0; foreach my $freq (keys %subtotals) { - next if $subtotals{$freq} == 0; + #next if $subtotals{$freq} == 0; + next if !$show{$freq}; my $weight = List::MoreUtils::first_index { $_ eq $freq } @pkg_freq_order; @@ -405,10 +409,10 @@ sub order { $cust_pkg->set( $_, $quotation_pkg->get($_) ); } - # currently only one discount each - my ($pkg_discount) = $quotation_pkg->quotation_pkg_discount; - if ( $pkg_discount ) { - $cust_pkg->set('discountnum', $pkg_discount->discountnum); + # can now have two discounts each (setup and recur) + foreach my $pkg_discount ($quotation_pkg->quotation_pkg_discount) { + my $field = $pkg_discount->setuprecur . '_discountnum'; + $cust_pkg->set($field, $pkg_discount->discountnum); } $all_cust_pkg{$cust_pkg} = []; # no services @@ -685,7 +689,7 @@ sub estimate { } my %quotation_pkg_tax; # quotationpkgnum => tax name => quotation_pkg_tax obj - my %quotation_pkg_discount; # quotationpkgnum => quotation_pkg_discount obj + my %quotation_pkg_discount; # quotationpkgnum => setuprecur => quotation_pkg_discount obj for (my $i = 0; $i < scalar(@return_bill); $i++) { my $this_bill = $return_bill[$i]->[0]; @@ -725,25 +729,25 @@ sub estimate { # discounts if ( $cust_bill_pkg->get('discounts') ) { + # discount records are generated as (setup, recur). + # well, not always, sometimes it's just (recur), but fixing this + # is horribly invasive. my $discount = $cust_bill_pkg->get('discounts')->[0]; + if ( $discount ) { - # discount records are generated as (setup, recur). - # well, not always, sometimes it's just (recur), but fixing this - # is horribly invasive. - my $qpd = $quotation_pkg_discount{$quotationpkgnum} + # find the quotation_pkg_discount record for this billing pass... + my $setuprecur = $i ? 'recur' : 'setup'; + my $qpd = $quotation_pkg_discount{$quotationpkgnum}{$setuprecur} ||= qsearchs('quotation_pkg_discount', { - 'quotationpkgnum' => $quotationpkgnum + 'quotationpkgnum' => $quotationpkgnum, + 'setuprecur' => $setuprecur, }); if (!$qpd) { #can't happen - warn "$me simulated bill returned a discount but no discount is in effect.\n"; + warn "$me simulated bill returned a $setuprecur discount but no discount is in effect.\n"; } - if ($discount and $qpd) { - if ( $i == 0 ) { - $qpd->set('setup_amount', $discount->amount); - } else { - $qpd->set('recur_amount', $discount->amount); - } + if ($qpd) { + $qpd->set('amount', $discount->amount); } } } # end of discount stuff @@ -812,10 +816,13 @@ sub estimate { return "$error (recording estimate for ".$quotation_pkg->part_pkg->pkg.")" if $error; } - foreach my $quotation_pkg_discount (values %quotation_pkg_discount) { - $error = $quotation_pkg_discount->replace; - return "$error (recording estimated discount)" - if $error; + foreach (values %quotation_pkg_discount) { + # { setup => one, recur => another } + foreach my $quotation_pkg_discount (values %$_) { + $error = $quotation_pkg_discount->replace; + return "$error (recording estimated discount)" + if $error; + } } foreach my $quotation_pkg_tax (map { values %$_ } values %quotation_pkg_tax) { $error = $quotation_pkg_tax->insert; diff --git a/FS/FS/quotation_pkg.pm b/FS/FS/quotation_pkg.pm index 10bdc2efe..49d0d9a5c 100644 --- a/FS/FS/quotation_pkg.pm +++ b/FS/FS/quotation_pkg.pm @@ -132,8 +132,8 @@ sub insert { my $error = $self->SUPER::insert; - if ( !$error and $self->discountnum ) { - warn "inserting discount #".$self->discountnum."\n"; + if ( !$error and ($self->setup_discountnum || $self->recur_discountnum) ) { + #warn "inserting discount\n"; $error = $self->insert_discount; $error .= ' (setting discount)' if $error; } @@ -262,28 +262,37 @@ sub insert_discount { #my ($self, %options) = @_; my $self = shift; - my $quotation_pkg_discount = FS::quotation_pkg_discount->new( { - 'quotationpkgnum' => $self->quotationpkgnum, - 'discountnum' => $self->discountnum, - #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, - } ); - - $quotation_pkg_discount->insert; + foreach my $x (qw(setup recur)) { + if ( my $discountnum = $self->get("${x}_discountnum") ) { + my $cust_pkg_discount = FS::quotation_pkg_discount->new( { + 'quotationpkgnum' => $self->quotationpkgnum, + 'discountnum' => $discountnum, + 'setuprecur' => $x, + #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; + } + } } sub _item_discount { my $self = shift; my %options = @_; my $setuprecur = $options{'setuprecur'}; + # a little different from cust_bill_pkg::_item_discount, in that this one + # is asked specifically whether to show setup or recur discounts (because + # on the quotation they're separate sections entirely) - # kind of silly treating this as multiple records, but it works, and will - # work if we allow multiple discounts at some point - my @pkg_discounts = $self->pkg_discount; + my @pkg_discounts = grep { $_->setuprecur eq $setuprecur } + $self->pkg_discount; return if @pkg_discounts == 0; my @ext; @@ -296,7 +305,7 @@ sub _item_discount { }; foreach my $pkg_discount (@pkg_discounts) { push @ext, $pkg_discount->description; - my $amount = $pkg_discount->get($setuprecur.'_amount'); + my $amount = $pkg_discount->get('amount'); $d->{amount} -= $amount; } $d->{amount} = sprintf('%.2f', $d->{amount} * $self->quantity); @@ -306,8 +315,12 @@ sub _item_discount { sub setup { my $self = shift; - ($self->unitsetup - sum(0, map { $_->setup_amount } $self->pkg_discount)) - * ($self->quantity || 1); + return '0.00' if $self->waive_setup eq 'Y';; + my $discount_amount = sum(0, map { $_->amount } + grep { $_->setuprecur eq 'setup' } + $self->pkg_discount + ); + ($self->unitsetup - $discount_amount) * ($self->quantity || 1); } @@ -318,8 +331,12 @@ sub setup_tax { sub recur { my $self = shift; - ($self->unitrecur - sum(0, map { $_->recur_amount } $self->pkg_discount)) - * ($self->quantity || 1) + my $discount_amount = sum(0, map { $_->amount } + grep { $_->setuprecur eq 'recur' } + $self->pkg_discount + ); + ($self->unitrecur - $discount_amount) * ($self->quantity || 1); + } sub recur_tax { diff --git a/FS/FS/quotation_pkg_discount.pm b/FS/FS/quotation_pkg_discount.pm index 4389db243..1815294ef 100644 --- a/FS/FS/quotation_pkg_discount.pm +++ b/FS/FS/quotation_pkg_discount.pm @@ -45,14 +45,14 @@ for. discountnum (L) -=item setup_amount +=item setuprecur -Amount that will be discounted from setup fees, per package quantity. +Whether this is a setup or recur discount. -=item recur_amount +=item amount -Amount that will be discounted from recurring fees in the first billing -cycle, per package quantity. +Amount that will be discounted from either setup or recur fees, per package +quantity. =back @@ -106,8 +106,8 @@ sub check { $self->ut_numbern('quotationpkgdiscountnum') || $self->ut_foreign_key('quotationpkgnum', 'quotation_pkg', 'quotationpkgnum' ) || $self->ut_foreign_key('discountnum', 'discount', 'discountnum' ) - || $self->ut_moneyn('setup_amount') - || $self->ut_moneyn('recur_amount') + || $self->ut_enum('setuprecur', ['setup', 'recur']) + || $self->ut_moneyn('amount') ; return $error if $error; diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 13bd2024b..62e9be5c3 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -526,6 +526,13 @@ sub pkg_status_row_discount { my $html; + if ( $cust_pkg->waive_setup ) { + my $label = '' . + emt('Setup fee waived') . + ''; + $html .= pkg_status_row_colspan( $cust_pkg, $label, '', %opt ); + } + foreach my $cust_pkg_discount (@{ $cust_pkg->{_cust_pkg_discount_active} }) { my $discount = $cust_pkg_discount->discount; @@ -537,7 +544,6 @@ sub pkg_status_row_discount { $label .= emt('Recurring Discount'); } $label .= ': '. $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 =~ /\./;