add cust_pay_refund table to refund payments
authorivan <ivan>
Tue, 29 Jun 2004 04:02:45 +0000 (04:02 +0000)
committerivan <ivan>
Tue, 29 Jun 2004 04:02:45 +0000 (04:02 +0000)
16 files changed:
ANNOUNCE.1.5.0 [new file with mode: 0644]
FS/FS.pm
FS/FS/cust_bill_pay.pm
FS/FS/cust_credit_bill.pm
FS/FS/cust_credit_refund.pm
FS/FS/cust_pay.pm
FS/FS/cust_pay_refund.pm [new file with mode: 0644]
FS/FS/cust_refund.pm
FS/MANIFEST
FS/bin/freeside-setup
FS/t/cust_pay_refund.t [new file with mode: 0644]
README.1.5.0pre6 [new file with mode: 0644]
httemplate/docs/upgrade10.html
httemplate/edit/cust_bill_pay.cgi
httemplate/edit/process/cust_bill_pay.cgi
httemplate/view/cust_main.cgi

diff --git a/ANNOUNCE.1.5.0 b/ANNOUNCE.1.5.0
new file mode 100644 (file)
index 0000000..d1c6f23
--- /dev/null
@@ -0,0 +1,10 @@
+- broadband (dsl/wireless) tracking, etc etc
+- Extended description on invoice for time/data charges
+- Multiple, named taxes
+- */*FIX
+- extended reported and graphing
+- integrated RT ticketing system
+- one-time payments (in signup server too).  DCRD and DCHK on-demand payment types
+- credit report
+- reseller interface
+- cust_pay_refund and credit card refund w/supported processor
index 6c560d9..67cb1f8 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -122,9 +122,11 @@ L<FS::cust_credit> - Credit class
 
 L<FS::cust_refund> - Refund class
 
-L<FS::cust_credit_refund> - Refund application class
+L<FS::cust_credit_refund> - Refund application to credit class
 
-L<FS::cust_credit_bill> - Credit invoice application class
+L<FS::cust_credit_bill> - Credit application to invoice class
+
+L<FS::cust_pay_refund> - Refund application to payment class
 
 L<FS::cust_pay_batch> - Credit card transaction queue class
 
index c8b5525..f0cb132 100644 (file)
@@ -74,60 +74,11 @@ otherwise returns false.
 
 sub insert {
   my $self = shift;
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE';
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
-
-  my $error = $self->check;
+  my $error = $self->SUPER::insert;
   return $error if $error;
 
-  $error = $self->SUPER::insert;
-
-  my $cust_pay = qsearchs('cust_pay', { 'paynum' => $self->paynum } ) or do {
-    $dbh->rollback if $oldAutoCommit;
-    return "unknown cust_pay.paynum: ". $self->paynum;
-  };
-
-  my $pay_total = 0;
-  $pay_total += $_ foreach map { $_->amount }
-    qsearch('cust_bill_pay', { 'paynum' => $self->paynum } );
-
-  if ( sprintf("%.2f", $pay_total) > sprintf("%.2f", $cust_pay->paid) ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "total cust_bill_pay.amount $pay_total for paynum ". $self->paynum.
-           " greater than cust_pay.paid ". $cust_pay->paid;
-  }
-
-  my $cust_bill = $self->cust_bill;
-  unless ( $cust_bill ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "unknown cust_bill.invnum: ". $self->invnum;
-  };
-
-  my $bill_total = 0;
-  $bill_total += $_ foreach map { $_->amount }
-    qsearch('cust_bill_pay', { 'invnum' => $self->invnum } );
-  $bill_total += $_ foreach map { $_->amount } 
-    qsearch('cust_credit_bill', { 'invnum' => $self->invnum } );
-  if ( sprintf("%.2f", $bill_total) > sprintf("%.2f", $cust_bill->charged) ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "total cust_bill_pay.amount and cust_credit_bill.amount $bill_total".
-           " for invnum ". $self->invnum.
-           " greater than cust_bill.charged ". $cust_bill->charged;
-  }
-
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
-  if ( $conf->exists('invoice_send_receipts') ) {
-    my $send_error = $cust_bill->send;
+ if ( $conf->exists('invoice_send_receipts') ) {
+    my $send_error = $self->cust_bill->send;
     warn "Error sending receipt: $send_error\n" if $send_error;
   }
 
@@ -178,9 +129,23 @@ sub check {
   return $error if $error;
 
   return "amount must be > 0" if $self->amount <= 0;
+  
+  return "Unknown invoice"
+    unless my $cust_bill =
+      qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+
+  return "Unknown payment"
+    unless my $cust_pay = 
+      qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
 
   $self->_date(time) unless $self->_date;
 
+  return "Cannot apply more than remaining value of invoice"
+    unless $self->amount <= $cust_bill->owed;
+
+  return "Cannot apply more than remaining value of payment"
+    unless $self->amount <= $cust_pay->unapplied;
+
   $self->SUPER::check;
 }
 
index bd76c2e..0e5885f 100644 (file)
@@ -38,7 +38,7 @@ FS::cust_credit_bill - Object methods for cust_credit_bill records
 =head1 DESCRIPTION
 
 An FS::cust_credit_bill object represents application of a credit (see
-L<FS::cust_credit>) to an invoice (see L<FS::cust_bill>).  FS::cust_credit
+L<FS::cust_credit>) to an invoice (see L<FS::cust_bill>).  FS::cust_credit_bill
 inherits from FS::Record.  The following fields are currently supported:
 
 =over 4
index d0deae2..ff2454d 100644 (file)
@@ -70,43 +70,9 @@ otherwise returns false.
 
 sub insert {
   my $self = shift;
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE';
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
-
-  my $error = $self->check;
+  my $error = $self->SUPER::insert;
   return $error if $error;
 
-  $error = $self->SUPER::insert;
-
-  my $cust_refund =
-    qsearchs('cust_refund', { 'refundnum' => $self->refundnum } )
-  or do {
-    $dbh->rollback if $oldAutoCommit;
-    return "unknown cust_refund.refundnum: ". $self->refundnum
-  };
-
-  my $refund_total = 0;
-  $refund_total += $_ foreach map { $_->amount }
-    qsearch('cust_credit_refund', { 'refundnum' => $self->refundnum } );
-
-  if ( $refund_total > $cust_refund->refund ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "total cust_credit_refund.amount $refund_total for refundnum ".
-           $self->refundnum.
-           " greater than cust_refund.refund ". $cust_refund->refund;
-  }
-
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
   '';
 }
 
@@ -132,8 +98,9 @@ sub replace {
 
 =item check
 
-Checks all fields to make sure this is a valid payment.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert method.
+Checks all fields to make sure this is a valid refund application.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+method.
 
 =cut
 
@@ -151,10 +118,21 @@ sub check {
 
   return "amount must be > 0" if $self->amount <= 0;
 
+  return "unknown cust_credit.crednum: ". $self->crednum
+    unless my $cust_credit =
+      qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+
+  return "Unknown refund"
+    unless my $cust_refund =
+      qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+
   $self->_date(time) unless $self->_date;
 
-  return "unknown cust_credit.crednum: ". $self->crednum
-    unless qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+  return "Cannot apply more than remaining value of credit"
+    unless $self->amount <= $cust_credit->credited;
+
+  return "Cannot apply more than remaining value of refund"
+    unless $self->amount <= $cust_refund->unapplied;
 
   $self->SUPER::check;
 }
@@ -185,7 +163,7 @@ sub cust_credit {
 
 =head1 VERSION
 
-$Id: cust_credit_refund.pm,v 1.10 2003-08-05 00:20:41 khoff Exp $
+$Id: cust_credit_refund.pm,v 1.11 2004-06-29 04:02:44 ivan Exp $
 
 =head1 BUGS
 
index ba9924f..111de05 100644 (file)
@@ -9,6 +9,7 @@ use FS::Record qw( dbh qsearch qsearchs dbh );
 use FS::Misc qw(send_email);
 use FS::cust_bill;
 use FS::cust_bill_pay;
+use FS::cust_pay_refund;
 use FS::cust_main;
 
 @ISA = qw( FS::Record );
@@ -371,10 +372,26 @@ sub cust_bill_pay {
   ;
 }
 
+=item cust_pay_refund
+
+Returns all applications of refunds (see L<FS::cust_pay_refund>) to this
+payment.
+
+=cut
+
+sub cust_pay_refund {
+  my $self = shift;
+  sort { $a->_date <=> $b->_date }
+    qsearch( 'cust_pay_refund', { 'paynum' => $self->paynum } )
+  ;
+}
+
+
 =item unapplied
 
 Returns the amount of this payment that is still unapplied; which is
-paid minus all payment applications (see L<FS::cust_bill_pay>).
+paid minus all payment applications (see L<FS::cust_bill_pay>) and refund
+applications (see L<FS::cust_pay_refund>).
 
 =cut
 
@@ -382,6 +399,7 @@ sub unapplied {
   my $self = shift;
   my $amount = $self->paid;
   $amount -= $_->amount foreach ( $self->cust_bill_pay );
+  $amount -= $_->amount foreach ( $self->cust_pay_refund );
   sprintf("%.2f", $amount );
 }
 
diff --git a/FS/FS/cust_pay_refund.pm b/FS/FS/cust_pay_refund.pm
new file mode 100644 (file)
index 0000000..55a5eb7
--- /dev/null
@@ -0,0 +1,187 @@
+package FS::cust_pay_refund;
+
+use strict;
+use vars qw( @ISA ); #$conf );
+use FS::UID qw( getotaker );
+use FS::Record qw( qsearchs ); # qsearch );
+use FS::cust_main;
+use FS::cust_pay;
+use FS::cust_refund;
+
+@ISA = qw( FS::Record );
+
+#ask FS::UID to run this stuff for us later
+#FS::UID->install_callback( sub { 
+#  $conf = new FS::Conf;
+#} );
+
+=head1 NAME
+
+FS::cust_pay_refund - Object methods for cust_pay_refund records
+
+=head1 SYNOPSIS
+
+  use FS::cust_pay_refund;
+
+  $record = new FS::cust_pay_refund \%hash;
+  $record = new FS::cust_pay_refund { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay_refund object represents application of a refund (see
+L<FS::cust_refund>) to an payment (see L<FS::cust_pay>).  FS::cust_pay_refund
+inherits from FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item payrefundnum - primary key
+
+=item paynum - credit being applied 
+
+=item refundnum - invoice to which credit is applied (see L<FS::cust_bill>)
+
+=item amount - amount of the credit applied
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">.  Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new cust_pay_refund.  To add the cust_pay_refund to the database,
+see L<"insert">.
+
+=cut
+
+sub table { 'cust_pay_refund'; }
+
+=item insert
+
+Adds this cust_pay_refund to the database.  If there is an error, returns the
+error, otherwise returns false.
+
+=cut
+
+sub insert {
+  my $self = shift;
+  my $error = $self->SUPER::insert(@_);
+  return $error if $error;
+
+  '';
+}
+
+=item delete
+
+=cut
+
+sub delete {
+  my $self = shift;
+  return "Can't apply refund to closed payment"
+    if $self->cust_pay->closed =~ /^Y/i;
+  return "Can't apply closed refund"
+    if $self->cust_refund->closed =~ /^Y/i;
+  $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Application of refunds to payments may not be modified.
+
+=cut
+
+sub replace {
+  return "Can't modify application of a refund to payment!"
+}
+
+=item check
+
+Checks all fields to make sure this is a valid refund application to a payment.
+If there is an error, returns the error, otherwise returns false.  Called by
+the insert and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error =
+    $self->ut_numbern('payrefundnum')
+    || $self->ut_number('paynum')
+    || $self->ut_number('refundnum')
+    || $self->ut_numbern('_date')
+    || $self->ut_money('amount')
+  ;
+  return $error if $error;
+
+  return "amount must be > 0" if $self->amount <= 0;
+
+  return "Unknown payment"
+    unless my $cust_pay = 
+      qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
+
+  return "Unknown refund"
+    unless my $cust_refund =
+      qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+
+  $self->_date(time) unless $self->_date;
+
+  return 'Cannot apply ($'. $self->amount. ') more than'.
+         ' remaining value of refund ($'. $cust_refund->unapplied. ')'
+    unless $self->amount <= $cust_refund->unapplied;
+
+  return "Cannot apply more than remaining value of payment"
+    unless $self->amount <= $cust_pay->unapplied;
+
+  $self->SUPER::check;
+}
+
+=item sub cust_credit
+
+Returns the credit (see L<FS::cust_credit>)
+
+=cut
+
+sub cust_credit {
+  my $self = shift;
+  qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+}
+
+=item cust_bill 
+
+Returns the invoice (see L<FS::cust_bill>)
+
+=cut
+
+sub cust_bill {
+  my $self = shift;
+  qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
+
+=back
+
+=head1 BUGS
+
+The delete method.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_refund>, L<FS::cust_bill>, L<FS::cust_credit>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
index d60c010..b9f48db 100644 (file)
@@ -3,10 +3,11 @@ package FS::cust_refund;
 use strict;
 use vars qw( @ISA );
 use Business::CreditCard;
-use FS::Record qw( qsearchs dbh );
+use FS::Record qw( qsearch qsearchs dbh );
 use FS::UID qw(getotaker);
 use FS::cust_credit;
 use FS::cust_credit_refund;
+use FS::cust_pay_refund;
 use FS::cust_main;
 
 @ISA = qw( FS::Record );
@@ -78,7 +79,9 @@ Adds this refund to the database.
 
 For backwards-compatibility and convenience, if the additional field crednum is
 defined, an FS::cust_credit_refund record for the full amount of the refund
-will be created.  In this case, custnum is optional.
+will be created.  Or (this time for convenience and consistancy), if the
+additional field paynum is defined, an FS::cust_pay_refund record for the full
+amount of the refund will be created.  In both cases, custnum is optional.
 
 =cut
 
@@ -103,6 +106,13 @@ sub insert {
         return "Unknown cust_credit.crednum: ". $self->crednum;
       };
     $self->custnum($cust_credit->custnum);
+  } elsif ( $self->paynum ) {
+    my $cust_pay = qsearchs('cust_pay', { 'paynum' => $self->paynum } )
+      or do {
+        $dbh->rollback if $oldAutoCommit;
+        return "Unknown cust_pay.paynum: ". $self->paynum;
+      };
+    $self->custnum($cust_pay->custnum);
   }
 
   my $error = $self->check;
@@ -127,57 +137,20 @@ sub insert {
       return $error;
     }
     #$self->custnum($cust_credit_refund->cust_credit->custnum);
-  }
-
-
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
-  '';
-
-}
-
-sub upgrade_replace { #1.3.x->1.4.x
-  my $self = shift;
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE';
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
-
-  my $error = $self->check;
-  return $error if $error;
-
-  my %new = $self->hash;
-  my $new = FS::cust_refund->new(\%new);
-
-  if ( $self->crednum ) {
-    my $cust_credit_refund = new FS::cust_credit_refund {
-      'crednum'   => $self->crednum,
+  } elsif ( $self->paynum ) {
+    my $cust_pay_refund = new FS::cust_pay_refund {
+      'paynum'    => $self->paynum,
       'refundnum' => $self->refundnum,
       'amount'    => $self->refund,
       '_date'     => $self->_date,
     };
-    $error = $cust_credit_refund->insert;
+    $error = $cust_pay_refund->insert;
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error;
     }
-    $new->custnum($cust_credit_refund->cust_credit->custnum);
-  } else {
-    die;
   }
 
-  $error = $new->SUPER::replace($self);
-  if ( $error ) {
-    $dbh->rollback if $oldAutoCommit;
-    return $error;
-  }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
@@ -263,6 +236,52 @@ sub check {
   $self->SUPER::check;
 }
 
+=item cust_credit_refund
+
+Returns all applications to credits (see L<FS::cust_credit_refund>) for this
+refund.
+
+=cut
+
+sub cust_credit_refund {
+  my $self = shift;
+  sort { $a->_date <=> $b->_date }
+    qsearch( 'cust_credit_refund', { 'refundnum' => $self->refundnum } )
+  ;
+}
+
+=item cust_pay_refund
+
+Returns all applications to payments (see L<FS::cust_pay_refund>) for this
+refund.
+
+=cut
+
+sub cust_pay_refund {
+  my $self = shift;
+  sort { $a->_date <=> $b->_date }
+    qsearch( 'cust_pay_refund', { 'refundnum' => $self->refundnum } )
+  ;
+}
+
+=item unapplied
+
+Returns the amount of this refund that is still unapplied; which is
+amount minus all credit applications (see L<FS::cust_credit_refund>) and
+payment applications (see L<FS::cust_pay_refund>).
+
+=cut
+
+sub unapplied {
+  my $self = shift;
+  my $amount = $self->refund;
+  $amount -= $_->amount foreach ( $self->cust_credit_refund );
+  $amount -= $_->amount foreach ( $self->cust_pay_refund );
+  sprintf("%.2f", $amount );
+}
+
+
+
 =item payinfo_masked
 
 Returns a "masked" payinfo field with all but the last four characters replaced
index 24fc1d1..4e78e72 100644 (file)
@@ -59,6 +59,7 @@ FS/cust_pay.pm
 FS/cust_bill_event.pm
 FS/cust_bill_pay.pm
 FS/cust_pay_batch.pm
+FS/cust_pay_refund.pm
 FS/cust_pkg.pm
 FS/cust_refund.pm
 FS/cust_credit_refund.pm
@@ -148,6 +149,7 @@ t/cust_main_county.t
 t/cust_main_invoice.t
 t/cust_pay.t
 t/cust_pay_batch.t
+t/cust_pay_refund.t
 t/cust_pkg.t
 t/cust_refund.t
 t/cust_svc.t
index 522c0a1..33b3465 100755 (executable)
@@ -1115,6 +1115,21 @@ sub tables_hash_hack {
       'index'       => [],
     },
 
+    'cust_pay_refund' => {
+      'columns' => [
+        'payrefundnum', 'serial', '', '',
+        'paynum',  'int', '', '',
+        'refundnum',  'int', '', '',
+        '_date',    @date_type,
+        'amount',   @money_type,
+      ],
+      'primary_key' => 'payrefundnum',
+      'unique' => [],
+      'index' => [ ['paynum'], ['refundnum'] ],
+    },
+
+
+
   );
 
   %tables;
diff --git a/FS/t/cust_pay_refund.t b/FS/t/cust_pay_refund.t
new file mode 100644 (file)
index 0000000..85d6c23
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay_refund;
+$loaded=1;
+print "ok 1\n";
diff --git a/README.1.5.0pre6 b/README.1.5.0pre6
new file mode 100644 (file)
index 0000000..ba9129f
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE TABLE cust_pay_refund (
+    payrefundnum serial NOT NULL,
+    paynum int NOT NULL,
+    refundnum int NOT NULL,
+    _date int NOT NULL,
+    amount decimal(10,2) NOT NULL,
+    PRIMARY KEY (payrefundnum)
+);
+CREATE INDEX cust_pay_refund1 ON cust_pay_refund(paynum);
+CREATE INDEX cust_pay_refund2 ON cust_pay_refund(refundnum);
+
+dbdef-create username
+create-history-tables username cust_pay_refund
+dbdef-create username
+
index c2b88be..1d09964 100644 (file)
@@ -113,8 +113,8 @@ select setval('public.part_pkg_temp_pkgpart_seq', ( select max(pkgpart) from par
 Or on Pg versions that don't support DROP CONSTRAINT and ADD PRIMARY KEY (tested on 7.1 and 7.2 so far):
 DROP INDEX part_pkg_temp_pkey;
 CREATE UNIQUE INDEX part_pkg_pkey ON part_pkg (pkgpart);
-7.1?: select setval('part_pkg_temp_pkgpart_seq', ( select max(pkgpart) from part_pkg) );
-7.2: select setval('part_pkg_pkgpart_seq', ( select max(pkgpart) from part_pkg) );
+probably this one?: select setval('part_pkg_temp_pkgpart_seq', ( select max(pkgpart) from part_pkg) );
+probably not this one?: select setval('part_pkg_pkgpart_seq', ( select max(pkgpart) from part_pkg) );
 
 CREATE TABLE h_part_pkg_temp (
     historynum serial NOT NULL,
@@ -148,9 +148,19 @@ select setval('public.h_part_pkg_temp_historynum_seq', ( select max(historynum)
 Or on Pg versions that don't support DROP CONSTRAINT and ADD PRIMARY KEY (tested on 7.1 and 7.2 so far):
 DROP INDEX h_part_pkg_temp_pkey;
 CREATE UNIQUE INDEX h_part_pkg_pkey ON h_part_pkg (historynum);
-7.1?: select setval('h_part_pkg_temp_historynum_seq', ( select max(historynum) from h_part_pkg) );
-7.2: select setval('h_part_pkg_historynum_seq', ( select max(historynum) from h_part_pkg) );
-
+probably this one?: select setval('h_part_pkg_temp_historynum_seq', ( select max(historynum) from h_part_pkg) );
+probably not this one?: select setval('h_part_pkg_historynum_seq', ( select max(historynum) from h_part_pkg) );
+
+CREATE TABLE cust_pay_refund (
+    payrefundnum serial NOT NULL,
+    paynum int NOT NULL,
+    refundnum int NOT NULL,
+    _date int NOT NULL,
+    amount decimal(10,2) NOT NULL,
+    PRIMARY KEY (payrefundnum)
+);
+CREATE INDEX cust_pay_refund1 ON cust_pay_refund(paynum);
+CREATE INDEX cust_pay_refund2 ON cust_pay_refund(refundnum);
 
 DROP INDEX cust_bill_pkg1;
 
index 8cdf450..24bce30 100755 (executable)
@@ -59,10 +59,10 @@ foreach my $cust_bill ( @cust_bill ) {
 END
 }
 
-#  if ( cust_bill == "Refund" ) {
-#    what.form.amount.value = "$credited";
-#  }
 print <<END;
+  if ( cust_bill == "Refund" ) {
+    what.form.amount.value = "$unapplied";
+  }
 }
 </SCRIPT>
 END
@@ -75,7 +75,7 @@ foreach my $cust_bill ( @cust_bill ) {
         ' -  '. time2str("%D",$cust_bill->_date).
         ' - $'. $cust_bill->owed;
 }
-#print qq!<OPTION VALUE="Refund">Refund!;
+print qq!<OPTION VALUE="Refund">Refund!;
 print "</SELECT>";
 
 print qq!<BR>Amount \$<INPUT TYPE="text" NAME="amount" VALUE="$amount" SIZE=8 MAXLENGTH=8>!;
index 0c33506..0025b16 100755 (executable)
@@ -11,12 +11,24 @@ my $cust_main = qsearchs('cust_main', { 'custnum' => $cust_pay->custnum } )
 
 my $custnum = $cust_main->custnum;
 
-my $new = new FS::cust_bill_pay ( {
-  map {
-    $_, scalar($cgi->param($_));
-  #} qw(custnum _date amount invnum)
-  } fields('cust_bill_pay')
-} );
+my $new;
+if ($cgi->param('invnum') =~ /^Refund$/) {
+  $new = new FS::cust_refund ( {
+    'reason'  => 'Refunding payment', #enter reason in UI
+    'refund'  => $cgi->param('amount'),
+    'payby'   => 'BILL',
+    #'_date'   => $cgi->param('_date'),
+    'payinfo' => 'Cash', #enter payinfo in UI
+    'paynum' => $paynum,
+  } );
+} else {
+  $new = new FS::cust_bill_pay ( {
+    map {
+      $_, scalar($cgi->param($_));
+    #} qw(custnum _date amount invnum)
+    } fields('cust_bill_pay')
+  } );
+}
 
 my $error = $new->insert;
 
index cf899d0..125c51a 100755 (executable)
@@ -604,6 +604,7 @@ function cust_credit_areyousure(href) {
                     ? $cust_pay->payinfo_masked
                     : $cust_pay->payinfo;
     my @cust_bill_pay = $cust_pay->cust_bill_pay;
+    my @cust_pay_refund = $cust_pay->cust_pay_refund;
 
     my $target = "$payby$payinfo";
     $payby =~ s/^BILL$/Check #/ if $payinfo;
@@ -612,25 +613,42 @@ function cust_credit_areyousure(href) {
     my $info = $payby ? " ($payby$payinfo)" : '';
 
     my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
-    if ( scalar(@cust_bill_pay) == 0 ) {
+    if (    scalar(@cust_bill_pay)   == 0
+         && scalar(@cust_pay_refund) == 0 ) {
       #completely unapplied
       $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
       $post = '</FONT></B>';
       $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!.
                $cust_pay->paynum. '">apply</A>)';
-    } elsif ( scalar(@cust_bill_pay) == 1 && $cust_pay->unapplied == 0 ) {
-      #applied to one invoice
+    } elsif (    scalar(@cust_bill_pay)   == 1
+              && scalar(@cust_pay_refund) == 0
+              && $cust_pay->unapplied == 0     ) {
+      #applied to one invoice, the usual situation
       $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum;
+    } elsif (    scalar(@cust_bill_pay)   == 0
+              && scalar(@cust_pay_refund) == 1
+              && $cust_pay->unapplied == 0     ) {
+      #applied to one refund
+      $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
     } else {
       #complicated
       $desc = '<BR>';
-      foreach my $cust_bill_pay (@cust_bill_pay) {
-        $desc .= '&nbsp;&nbsp;'.
-                 '$'. $cust_bill_pay->amount.
-                 ' applied to Invoice #'. $cust_bill_pay->invnum.
-                 '<BR>';
-                 #' on '. time2str("%D", $cust_bill_pay->_date).
-
+      foreach my $app ( sort { $a->_date <=> $b->_date }
+                             ( @cust_bill_pay, @cust_pay_refund ) ) {
+        if ( $app->isa('FS::cust_bill_pay') ) {
+          $desc .= '&nbsp;&nbsp;'.
+                   '$'. $app->amount.
+                   ' applied to Invoice #'. $app->invnum.
+                   '<BR>';
+                   #' on '. time2str("%D", $cust_bill_pay->_date).
+        } elsif ( $app->isa('FS::cust_pay_refund') ) {
+          $desc .= '&nbsp;&nbsp;'.
+                   '$'. $app->amount.
+                   ' refunded on'. time2str("%D", $app->_date).
+                   '<BR>';
+        } else {
+          die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
+        }
       }
       if ( $cust_pay->unapplied > 0 ) {
         $desc .= '&nbsp;&nbsp;'.
@@ -684,7 +702,7 @@ function cust_credit_areyousure(href) {
     } elsif (    scalar(@cust_credit_bill)   == 1
               && scalar(@cust_credit_refund) == 0
               && $cust_credit->credited == 0      ) {
-      #applied to one invoice
+      #applied to one invoice, the usual situation
       $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum;
     } elsif (    scalar(@cust_credit_bill)   == 0
               && scalar(@cust_credit_refund) == 1