summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2006-08-21 23:01:43 +0000
committerivan <ivan>2006-08-21 23:01:43 +0000
commit4845aaefedfcbaa25716694b6d80f3dd6d56530a (patch)
tree2f4f07b0f10f49f1f1e36d3612c273ce5cbd613e
parentf26658d1aa536ba0c5a60e8a8d22781ba2be682a (diff)
add cust_bill_pay_pkg and cust_credit_bill_pkg - applying credits and payments against specific line items
-rw-r--r--FS/FS/Schema.pm26
-rw-r--r--FS/FS/cust_bill.pm27
-rw-r--r--FS/FS/cust_bill_ApplicationCommon.pm243
-rw-r--r--FS/FS/cust_bill_pay.pm55
-rw-r--r--FS/FS/cust_bill_pay_pkg.pm132
-rw-r--r--FS/FS/cust_bill_pkg.pm70
-rw-r--r--FS/FS/cust_credit_bill.pm47
-rw-r--r--FS/FS/cust_credit_bill_pkg.pm132
-rw-r--r--FS/MANIFEST4
-rw-r--r--FS/t/cust_bill_ApplicationCommon.t5
-rw-r--r--FS/t/cust_bill_pay_pkg.t5
-rw-r--r--FS/t/cust_credit_bill_pkg.t5
12 files changed, 684 insertions, 67 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index ce2d790..b3d8a56 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -390,6 +390,19 @@ sub tables_hashref {
'index' => [ ['crednum'], ['invnum'] ],
},
+ 'cust_credit_bill_pkg' => {
+ 'columns' => [
+ 'creditbillpkgnum', 'serial', '', '', '', '',
+ 'creditbillnum', 'int', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ 'setuprecur', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'creditbillpkgnum',
+ 'unique' => [],
+ 'index' => [ [ 'creditbillnum' ], [ 'billpkgnum' ], ],
+ },
+
'cust_main' => {
'columns' => [
'custnum', 'serial', '', '', '', '',
@@ -540,6 +553,19 @@ sub tables_hashref {
'index' => [ [ 'paynum' ], [ 'invnum' ] ],
},
+ 'cust_bill_pay_pkg' => {
+ 'columns' => [
+ 'billpaypkgnum', 'serial', '', '', '', '',
+ 'billpaynum', 'int', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ 'setuprecur', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'billpaypkgnum',
+ 'unique' => [],
+ 'index' => [ [ 'billpaynum' ], [ 'billpkgnum' ], ],
+ },
+
'pay_batch' => { #batches of payments to an external processor
'columns' => [
'batchnum', 'serial', '', '', '', '',
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index d45b66f..a93d175 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -225,6 +225,33 @@ sub cust_bill_pkg {
qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
}
+=item open_cust_bill_pkg
+
+Returns the open line items for this invoice.
+
+Note that cust_bill_pkg with both setup and recur fees are returned as two
+separate line items, each with only one fee.
+
+=cut
+
+# modeled after cust_main::open_cust_bill
+sub open_cust_bill_pkg {
+ my $self = shift;
+
+ # grep { $_->owed > 0 } $self->cust_bill_pkg
+
+ my %other = ( 'recur' => 'setup',
+ 'setup' => 'recur', );
+ my @open = ();
+ foreach my $field ( qw( recur setup )) {
+ push @open, map { $_->set( $other{$field}, 0 ); $_; }
+ grep { $_->owed($field) > 0 }
+ $self->cust_bill_pkg;
+ }
+
+ @open;
+}
+
=item cust_bill_event
Returns the completed invoice events (see L<FS::cust_bill_event>) for this
diff --git a/FS/FS/cust_bill_ApplicationCommon.pm b/FS/FS/cust_bill_ApplicationCommon.pm
new file mode 100644
index 0000000..fb06a4b
--- /dev/null
+++ b/FS/FS/cust_bill_ApplicationCommon.pm
@@ -0,0 +1,243 @@
+package FS::cust_bill_ApplicationCommon;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 1;
+
+=head1 NAME
+
+FS::cust_bill_ApplicationCommon - Base class for bill application classes
+
+=head1 SYNOPSIS
+
+use FS::cust_bill_ApplicationCommon;
+
+@ISA = qw( FS::cust_bill_ApplicationCommon );
+
+sub _app_source_name { 'payment'; }
+sub _app_source_table { 'cust_pay'; }
+sub _app_lineitem_breakdown_table { 'cust_bill_pay_pkg'; }
+
+=head1 DESCRIPTION
+
+FS::cust_bill_ApplicationCommon is intended as a base class for classes which
+represent application of things to invoices, currently payments
+(see L<FS::cust_bill_pay>) or credits (see L<FS::cust_credit_bill>).
+
+=head1 METHODS
+
+=item insert
+
+=cut
+
+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->SUPER::insert(@_)
+ || $self->apply_to_lineitems;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+=cut
+
+sub delete {
+ 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;
+
+ foreach my $app ( $self->lineitem_applications ) {
+ my $error = $app->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item apply_to_lineitems
+
+Auto-applies this invoice application to specific line items, if possible.
+
+=cut
+
+sub apply_to_lineitems {
+ my $self = shift;
+
+ my @apply = ();
+
+ 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 @open = $self->cust_bill->open_cust_bill_pkg; #FOR UPDATE...?
+ warn scalar(@open). " open line items for invoice ".
+ $self->cust_bill->invnum. "\n"
+ if $DEBUG;
+ my $total = 0;
+ $total += $_->setup + $_->recur foreach @open;
+ $total = sprintf('%.2f', $total);
+
+ if ( $self->amount > $total ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't apply a ". $self->_app_source_name. ' of $'. $self->amount.
+ " greater than the remaining owed on line items (\$$total)";
+ }
+
+ #easy cases:
+ # - one lineitem (a simple special case of:)
+ # - amount is for whole invoice (well, all of remaining lineitem links)
+ if ( $self->amount == $total ) {
+
+ #@apply = map { [ $_, $_->amount ]; } @open;
+ @apply = map { [ $_, $_->setup || $_->recur ]; } @open;
+
+ } else {
+
+ #slightly magic case:
+ # - amount exactly and uniquely matches a single open lineitem
+ # (you must be trying to pay or credit that item, then)
+
+ my @same = grep { $_->setup == $self->amount
+ || $_->recur == $self->amount
+ }
+ @open;
+ @apply = map { [ $_, $self->amount ]; } @same
+ if scalar(@same) == 1;
+
+ }
+
+ #and the rest:
+ # - leave unapplied, for now
+ # - eventually, auto-apply? sequentially? pro-rated against total remaining?
+
+ # do the applicaiton(s)
+ my $table = $self->lineitem_breakdown_table;
+ my $source_key = dbdef->table($self->table)->primary_key;
+ foreach my $apply ( @apply ) {
+ my ( $cust_bill_pkg, $amount ) = @$apply;
+ my $application = "FS::$table"->new( {
+ $source_key => $self->$source_key(),
+ 'billpkgnum' => $cust_bill_pkg->billpkgnum,
+ 'amount' => $amount,
+ 'setuprecur' => ( $cust_bill_pkg->setup > 0 ? 'setup' : 'recur' ),
+ });
+ my $error = $application->insert;
+ if ( $error ) {
+ dbh->rollbck if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ '';
+
+}
+
+=item lineitem_applications
+
+Returns all the specific line item applications for this invoice application.
+
+=cut
+
+sub lineitem_applications {
+ my $self = shift;
+ my $primary_key = dbdef->table($self->table)->primary_key;
+ qsearchs({
+ 'table' => $self->lineitem_breakdown_table,
+ 'hashref' => { $primary_key => $self->$primary_key() },
+ });
+
+}
+
+=item cust_bill
+
+Returns the invoice (see L<FS::cust_bill>)
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
+
+=item lineitem_breakdown_table
+
+=cut
+
+sub lineitem_breakdown_table {
+ my $self = shift;
+ $self->_load_table($self->_app_lineitem_breakdown_table);
+}
+
+sub _load_table {
+ my( $self, $table ) = @_;
+ eval "use FS::$table";
+ die $@ if $@;
+ $table;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_bill_pay> and L<FS::cust_bill_pay_pkg>,
+L<FS::cust_credit_bill> and L<FS::cust_credit_bill_pkg>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pay.pm b/FS/FS/cust_bill_pay.pm
index 7abbe9a..67f8eaf 100644
--- a/FS/FS/cust_bill_pay.pm
+++ b/FS/FS/cust_bill_pay.pm
@@ -2,11 +2,12 @@ package FS::cust_bill_pay;
use strict;
use vars qw( @ISA $conf );
-use FS::Record qw( qsearch qsearchs dbh );
+use FS::Record qw( qsearchs );
+use FS::cust_bill_ApplicationCommon;
use FS::cust_bill;
use FS::cust_pay;
-@ISA = qw( FS::Record );
+@ISA = qw( FS::cust_bill_ApplicationCommon );
#ask FS::UID to run this stuff for us later
FS::UID->install_callback( sub {
@@ -35,8 +36,9 @@ FS::cust_bill_pay - Object methods for cust_bill_pay records
=head1 DESCRIPTION
An FS::cust_bill_pay object represents the application of a payment to a
-specific invoice. FS::cust_bill_pay inherits from FS::Record. The following
-fields are currently supported:
+specific invoice. FS::cust_bill_pay inherits from
+FS::cust_bill_ApplicationCommon and FS::Record. The following fields are
+currently supported:
=over 4
@@ -65,6 +67,10 @@ Creates a new record. To add the record to the database, see L<"insert">.
sub table { 'cust_bill_pay'; }
+sub _app_source_name { 'payment'; }
+sub _app_source_table { 'cust_pay'; }
+sub _app_lineitem_breakdown_table { 'cust_bill_pay_pkg'; }
+
=item insert
Adds this record to the database. If there is an error, returns the error,
@@ -81,6 +87,8 @@ sub delete {
my $self = shift;
return "Can't delete application for closed payment"
if $self->cust_pay->closed =~ /^Y/i;
+ return "Can't delete application for closed invoice"
+ if $self->cust_bill->closed =~ /^Y/i;
$self->SUPER::delete(@_);
}
@@ -91,13 +99,14 @@ Currently unimplemented (accounting reasons).
=cut
sub replace {
- return "Can't (yet?) modify cust_bill_pay records!";
+ return "Can't modify application of payment!";
}
=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 payment application. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+method.
=cut
@@ -106,30 +115,22 @@ sub check {
my $error =
$self->ut_numbern('billpaynum')
- || $self->ut_number('invnum')
- || $self->ut_number('paynum')
- || $self->ut_money('amount')
+ || $self->ut_foreign_key('paynum', 'cust_pay', 'paynum' )
+ || $self->ut_foreign_key('invnum', 'cust_bill', 'invnum' )
|| $self->ut_numbern('_date')
+ || $self->ut_money('amount')
;
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;
+ unless $self->amount <= $self->cust_bill->owed;
return "Cannot apply more than remaining value of payment"
- unless $self->amount <= $cust_pay->unapplied;
+ unless $self->amount <= $self->cust_pay->unapplied;
$self->SUPER::check;
}
@@ -145,26 +146,12 @@ sub cust_pay {
qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
}
-=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
Delete and replace methods.
-the checks for over-applied payments could be better done like the ones in
-cust_bill_credit
-
=head1 SEE ALSO
L<FS::cust_pay>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
diff --git a/FS/FS/cust_bill_pay_pkg.pm b/FS/FS/cust_bill_pay_pkg.pm
new file mode 100644
index 0000000..af331cd
--- /dev/null
+++ b/FS/FS/cust_bill_pay_pkg.pm
@@ -0,0 +1,132 @@
+package FS::cust_bill_pay_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_bill_pay_pkg - Object methods for cust_bill_pay_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pay_pkg;
+
+ $record = new FS::cust_bill_pay_pkg \%hash;
+ $record = new FS::cust_bill_pay_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pay_pkg object represents application of a payment (see
+L<FS::cust_bill_pay>) to a specific line item within an invoice (see
+L<FS::cust_bill_pkg>). FS::cust_bill_pay_pkg inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item billpaypkgnum - primary key
+
+=item billpaynum - Payment application to the overall invoice (see L<FS::cust_bill_pay>)
+
+=item billpkgnum - Line item to which payment is applied (see L<FS::cust_bill_pkg>)
+
+=item amount - Amount of the payment applied to this line item.
+
+=item setuprecur - 'setup' or 'recur', designates whether the payment was applied to the setup or recurring portion of the line item.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_bill_pay_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid payment application. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpaypkgnum')
+ || $self->ut_foreign_key('billpaynum', 'cust_bill_pay', 'billpaynum' )
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+ || $self->ut_money('amount')
+ || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+B<setuprecur> field is a kludge to compensate for cust_bill_pkg having separate
+setup and recur fields. It should be removed once that's fixed.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index d718b05..e41a3c5 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -7,6 +7,8 @@ use FS::cust_main_Mixin;
use FS::cust_pkg;
use FS::cust_bill;
use FS::cust_bill_pkg_detail;
+use FS::cust_bill_pay_pkg;
+use FS::cust_credit_bill_pkg;
@ISA = qw( FS::cust_main_Mixin FS::Record );
@@ -224,18 +226,78 @@ sub desc {
}
}
-=back
+=item owed_setup
-=head1 CLASS METHODS
+Returns the amount owed (still outstanding) on this line item's setup fee,
+which is the amount of the line item minus all payment applications (see
+L<FS::cust_bill_pay_pkg> and credit applications (see
+L<FS::cust_credit_bill_pkg>).
-=over 4
+=cut
+
+sub owed_setup {
+ my $self = shift;
+ $self->owed('setup', @_);
+}
-=item
+=item owed_recur
+
+Returns the amount owed (still outstanding) on this line item's recurring fee,
+which is the amount of the line item minus all payment applications (see
+L<FS::cust_bill_pay_pkg> and credit applications (see
+L<FS::cust_credit_bill_pkg>).
+
+=cut
+
+sub owed_recur {
+ my $self = shift;
+ $self->owed('recur', @_);
+}
+
+# modeled after cust_bill::owed...
+sub owed {
+ my( $self, $field ) = @_;
+ my $balance = $self->$field();
+ $balance -= $_->amount foreach ( $self->cust_bill_pay_pkg($field) );
+ $balance -= $_->amount foreach ( $self->cust_credit_bill_pkg($field) );
+ $balance = sprintf( '%.2f', $balance );
+ $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
+ $balance;
+}
+
+sub cust_bill_pay_pkg {
+ my( $self, $field ) = @_;
+ qsearch( 'cust_bill_pay_pkg', { 'billpkgnum' => $self->billpkgnum,
+ 'setuprecur' => $field,
+ }
+ );
+}
+
+sub cust_credit_bill_pkg {
+ my( $self, $field ) = @_;
+ qsearch( 'cust_credit_bill_pkg', { 'billpkgnum' => $self->billpkgnum,
+ 'setuprecur' => $field,
+ }
+ );
+}
=back
=head1 BUGS
+setup and recur shouldn't be separate fields. There should be one "amount"
+field and a flag to tell you if it is a setup/one-time fee or a recurring fee.
+
+A line item with both should really be two separate records (preserving
+sdate and edate for setup fees for recurring packages - that information may
+be valuable later). Invoice generation (cust_main::bill), invoice printing
+(cust_bill), tax reports (report_tax.cgi) and line item reports
+(cust_bill_pkg.cgi) would need to be updated.
+
+owed_setup and owed_recur could then be repaced by just owed, and
+cust_bill::open_cust_bill_pkg and
+cust_bill_ApplicationCommon::apply_to_lineitems could be simplified.
+
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_bill>, L<FS::cust_pkg>, L<FS::cust_main>, schema.html
diff --git a/FS/FS/cust_credit_bill.pm b/FS/FS/cust_credit_bill.pm
index 695df6e..c015ec1 100644
--- a/FS/FS/cust_credit_bill.pm
+++ b/FS/FS/cust_credit_bill.pm
@@ -4,12 +4,11 @@ use strict;
use vars qw( @ISA $conf );
use FS::UID qw( getotaker );
use FS::Record qw( qsearch qsearchs );
-use FS::cust_main;
-#use FS::cust_refund;
-use FS::cust_credit;
+use FS::cust_bill_ApplicationCommon;
use FS::cust_bill;
+use FS::cust_credit;
-@ISA = qw( FS::Record );
+@ISA = qw( FS::cust_bill_ApplicationCommon );
#ask FS::UID to run this stuff for us later
FS::UID->install_callback( sub {
@@ -39,7 +38,8 @@ FS::cust_credit_bill - Object methods for cust_credit_bill records
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_bill
-inherits from FS::Record. The following fields are currently supported:
+inherits from FS::cust_bill_ApplicationCommon and FS::Record. The following
+fields are currently supported:
=over 4
@@ -69,6 +69,10 @@ see L<"insert">.
sub table { 'cust_credit_bill'; }
+sub _app_source_name { 'credit'; }
+sub _app_source_table { 'cust_credit'; }
+sub _app_lineitem_breakdown_table { 'cust_credit_bill_pkg'; }
+
=item insert
Adds this cust_credit_bill to the database ("Posts" all or part of a credit).
@@ -84,6 +88,8 @@ sub delete {
my $self = shift;
return "Can't delete application for closed credit"
if $self->cust_credit->closed =~ /^Y/i;
+ return "Can't delete application for closed invoice"
+ if $self->cust_bill->closed =~ /^Y/i;
$self->SUPER::delete(@_);
}
@@ -110,8 +116,8 @@ sub check {
my $error =
$self->ut_numbern('creditbillnum')
- || $self->ut_number('crednum')
- || $self->ut_number('invnum')
+ || $self->ut_foreign_key('crednum', 'cust_credit', 'crednum')
+ || $self->ut_foreign_key('invnum', 'cust_bill', 'invnum' )
|| $self->ut_numbern('_date')
|| $self->ut_money('amount')
;
@@ -119,21 +125,13 @@ sub check {
return "amount must be > 0" if $self->amount <= 0;
- return "Unknown credit"
- unless my $cust_credit =
- qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
-
- return "Unknown invoice"
- unless my $cust_bill =
- qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
-
$self->_date(time) unless $self->_date;
return "Cannot apply more than remaining value of credit"
- unless $self->amount <= $cust_credit->credited;
+ unless $self->amount <= $self->cust_credit->credited;
return "Cannot apply more than remaining value of invoice"
- unless $self->amount <= $cust_bill->owed;
+ unless $self->amount <= $self->cust_bill->owed;
$self->SUPER::check;
}
@@ -149,26 +147,17 @@ sub cust_credit {
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.
+This probably should have been called cust_bill_credit.
+
=head1 SEE ALSO
-L<FS::Record>, L<FS::cust_refund>, L<FS::cust_bill>, L<FS::cust_credit>,
+L<FS::Record>, L<FS::cust_bill>, L<FS::cust_credit>,
schema.html from the base documentation.
=cut
diff --git a/FS/FS/cust_credit_bill_pkg.pm b/FS/FS/cust_credit_bill_pkg.pm
new file mode 100644
index 0000000..98521d6
--- /dev/null
+++ b/FS/FS/cust_credit_bill_pkg.pm
@@ -0,0 +1,132 @@
+package FS::cust_credit_bill_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_credit_bill_pkg - Object methods for cust_credit_bill_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_credit_bill_pkg;
+
+ $record = new FS::cust_credit_bill_pkg \%hash;
+ $record = new FS::cust_credit_bill_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_credit_bill_pkg object represents application of a credit (see
+L<FS::cust_credit_bill>) to a specific line item within an invoice
+(see L<FS::cust_bill_pkg>). FS::cust_credit_bill_pkg inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item creditbillpkg - primary key
+
+=item creditbillnum - Credit application to the overall invoice (see L<FS::cust_credit::bill>)
+
+=item billpkgnum - Line item to which credit is applied (see L<FS::cust_bill_pkg>)
+
+=item amount - Amount of the credit applied to this line item.
+
+=item setuprecur - 'setup' or 'recur', designates whether the payment was applied to the setup or recurring portion of the line item.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_credit_bill_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid credit applicaiton. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('creditbillpkgnum')
+ || $self->ut_foreign_key('creditbillnum', 'cust_credit_bill', 'creditbillnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+ || $self->ut_money('amount')
+ || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+B<setuprecur> field is a kludge to compensate for cust_bill_pkg having separate
+setup and recur fields. It should be removed once that's fixed.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 42b6165..10f9ffa 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -350,3 +350,7 @@ FS/CurrentUser.pm
FS/svc_phone.pm
t/svc_phone.t
FS/h_svc_phone.pm
+FS/cust_bill_pay_pkg.pm
+t/cust_bill_pay_pkg.t
+FS/cust_credit_bill_pkg.pm
+t/cust_credit_bill_pkg.t
diff --git a/FS/t/cust_bill_ApplicationCommon.t b/FS/t/cust_bill_ApplicationCommon.t
new file mode 100644
index 0000000..fa03d34
--- /dev/null
+++ b/FS/t/cust_bill_ApplicationCommon.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_ApplicationCommon;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pay_pkg.t b/FS/t/cust_bill_pay_pkg.t
new file mode 100644
index 0000000..b8fcddb
--- /dev/null
+++ b/FS/t/cust_bill_pay_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pay_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_credit_bill_pkg.t b/FS/t/cust_credit_bill_pkg.t
new file mode 100644
index 0000000..4eb84c3
--- /dev/null
+++ b/FS/t/cust_credit_bill_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_credit_bill_pkg;
+$loaded=1;
+print "ok 1\n";