Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Wed, 28 Oct 2015 18:24:46 +0000 (11:24 -0700)
committerIvan Kohler <ivan@freeside.biz>
Wed, 28 Oct 2015 18:24:46 +0000 (11:24 -0700)
FS/FS/ClientAPI/MyAccount.pm
FS/FS/ClientAPI/Signup.pm
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/cust_credit.pm
FS/FS/part_pkg/discount_Mixin.pm
FS/FS/quotation.pm
FS/FS/quotation_pkg.pm
FS/FS/quotation_pkg_discount.pm
httemplate/view/cust_main/packages/status.html

index 98b87ad..89c70f7 100644 (file)
@@ -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 };
index c0a9d98..a4ea21b 100644 (file)
@@ -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;
index 594c0e0..990f2a3 100644 (file)
@@ -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'),
   },
index 7dc54f7..447302b 100644 (file)
@@ -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',
index 31adebe..2f2338e 100644 (file)
@@ -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
index 1e39f6a..dcca343 100644 (file)
@@ -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);
 
index d66b1b8..c400493 100644 (file)
@@ -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;
index 10bdc2e..49d0d9a 100644 (file)
@@ -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 {
index 4389db2..1815294 100644 (file)
@@ -45,14 +45,14 @@ for.
 
 discountnum (L<FS::discount>)
 
-=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;
 
index 13bd202..62e9be5 100644 (file)
@@ -526,6 +526,13 @@ sub pkg_status_row_discount {
 
   my $html;
 
+  if ( $cust_pkg->waive_setup ) {
+    my $label = '<SPAN STYLE="font-size: small;font-weight: bold">' .
+                 emt('Setup fee waived') .
+                 '</SPAN>';
+    $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 .= '</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 =~ /\./;