'eventnum', 'int', '', '', '', '',
'billpkgnum', 'int', 'NULL', '', '', '',
'feepart', 'int', '', '', '', '',
+ 'nextbill', 'char', 'NULL', 1, '', '',
],
'primary_key' => 'eventfeenum', # I'd rather just use eventnum
'unique' => [ [ 'billpkgnum' ], [ 'eventnum' ] ], # one-to-one link
=head1 DESCRIPTION
An FS::cust_bill_pkg_fee object records the origin of a fee.
-. FS::cust_bill_pkg_fee inherits from
-FS::Record. The following fields are currently supported:
+FS::cust_bill_pkg_fee inherits from FS::Record. The following fields
+are currently supported:
=over 4
my $error =
$self->ut_numbern('billpkgfeenum')
|| $self->ut_number('billpkgnum')
- || $self->ut_foreign_key('origin_invnum', 'cust_bill', 'invnum')
- || $self->ut_foreign_keyn('origin_billpkgnum', 'cust_bill_pkg', 'billpkgnum')
+ || $self->ut_foreign_key('base_invnum', 'cust_bill', 'invnum')
+ || $self->ut_foreign_keyn('base_billpkgnum', 'cust_bill_pkg', 'billpkgnum')
|| $self->ut_money('amount')
;
return $error if $error;
=item feepart - key of the fee definition (L<FS::part_fee>).
+=item nextbill - 'Y' if the fee should be charged on the customer's next
+bill, rather than causing a bill to be produced immediately.
+
=back
=head1 METHODS
|| $self->ut_foreign_key('eventnum', 'cust_event', 'eventnum')
|| $self->ut_foreign_keyn('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
|| $self->ut_foreign_key('feepart', 'part_fee', 'feepart')
+ || $self->ut_flag('nextbill')
;
return $error if $error;
my @cust_bill_pkg = _omit_zero_value_bundles(@{ $cust_bill_pkg{$pass} });
- next unless @cust_bill_pkg; #don't create an invoice w/o line items
-
warn "$me billing pass $pass\n"
#.Dumper(\@cust_bill_pkg)."\n"
if $DEBUG > 2;
hashref => { 'billpkgnum' => '' }
);
warn "$me found pending fee events:\n".Dumper(\@pending_event_fees)."\n"
- if @pending_event_fees;
+ if @pending_event_fees and $DEBUG > 1;
+
+ # determine whether to generate an invoice
+ my $generate_bill = scalar(@cust_bill_pkg) > 0;
+
+ foreach my $event_fee (@pending_event_fees) {
+ $generate_bill = 1 unless $event_fee->nextbill;
+ }
+
+ # don't create an invoice with no line items, or where the only line
+ # items are fees that are supposed to be held until the next invoice
+ next if !$generate_bill;
+ # calculate fees...
my @fee_items;
foreach my $event_fee (@pending_event_fees) {
my $object = $event_fee->cust_event->cust_X;
+ my $part_fee = $event_fee->part_fee;
my $cust_bill;
if ( $object->isa('FS::cust_main') ) {
# Not the real cust_bill object that will be inserted--in particular
# etc.)
$cust_bill = $object;
}
- my $part_fee = $event_fee->part_fee;
# if the fee def belongs to a different agent, don't charge the fee.
# event conditions should prevent this, but just in case they don't,
# skip the fee.
# link this so that we can clear the marker on inserting the line item
$fee_item->set('cust_event_fee', $event_fee);
push @fee_items, $fee_item;
+
}
+
+ # add fees to the invoice
foreach my $fee_item (@fee_items) {
push @cust_bill_pkg, $fee_item;
use strict;
use base qw( FS::part_event::Action );
+use FS::Record qw( qsearch );
sub event_stage { 'pre-bill'; }
value_col => 'feepart',
disable_empty => 1,
},
- );
+ ),
+
}
sub default_weight { 10; }
+sub hold_until_bill { 1 }
+
sub do_action {
my( $self, $cust_object, $cust_event ) = @_;
- die "no fee definition selected for event '".$self->event."'\n"
- unless $self->option('feepart');
+ my $feepart = $self->option('feepart')
+ or die "no fee definition selected for event '".$self->event."'\n";
+ my $tablenum = $cust_object->get($cust_object->primary_key);
+
+ # see if there's already a pending fee for this customer/invoice
+ my @existing = qsearch({
+ table => 'cust_event_fee',
+ addl_from => 'JOIN cust_event USING (eventnum)',
+ hashref => { feepart => $feepart,
+ billpkgnum => '' },
+ extra_sql => " AND tablenum = $tablenum",
+ });
+ if (scalar @existing > 0) {
+ warn $self->event." event, object $tablenum: already scheduled\n"
+ if $FS::part_fee::DEBUG;
+ return;
+ }
# mark the event so that the fee will be charged
# the logic for calculating the fee amount is in FS::part_fee
# FS::cust_bill_pkg
my $cust_event_fee = FS::cust_event_fee->new({
'eventnum' => $cust_event->eventnum,
- 'feepart' => $self->option('feepart'),
+ 'feepart' => $feepart,
'billpkgnum' => '',
+ 'nextbill' => $self->hold_until_bill ? 'Y' : '',
});
my $error = $cust_event_fee->insert;
{ 'cust_bill' => 1 };
}
+sub option_fields {
+ (
+ __PACKAGE__->SUPER::option_fields,
+ 'nextbill' => { label => 'Hold fee until the customer\'s next bill',
+ type => 'checkbox',
+ value => 'Y'
+ },
+ )
+}
+
+# it makes sense for this to be optional for previous-invoice fees
+sub hold_until_bill {
+ my $self = shift;
+ $self->option('nextbill');
+}
+
1;
{ 'cust_main' => 1 };
}
+sub hold_until_bill { 1 }
+
# Otherwise identical to cust_bill_fee. We only have a separate event
# because it behaves differently as an invoice event than as a customer
# event, and needs a different description.
use vars qw( $DEBUG );
use FS::Record qw( qsearch qsearchs );
-$DEBUG = 1;
+$DEBUG = 0;
=head1 NAME
my $self = shift;
$self->set('amount', 0) unless $self->amount;
+ $self->set('percent', 0) unless $self->percent;
my $error =
$self->ut_numbern('feepart')
|| $self->ut_floatn('credit_weight')
|| $self->ut_agentnum_acl('agentnum',
[ 'Edit global package definitions' ])
- || $self->ut_moneyn('amount')
- || $self->ut_floatn('percent')
+ || $self->ut_money('amount')
+ || $self->ut_float('percent')
|| $self->ut_moneyn('minimum')
|| $self->ut_moneyn('maximum')
|| $self->ut_flag('limit_credit')
$maximum = -1 * $balance;
}
}
- if ( $maximum ne '' ) {
+ if ( $maximum ne '' and $amount > $maximum ) {
warn "Applying maximum fee\n" if $DEBUG;
$amount = $maximum;
}
});
if ( $maximum and $self->taxable ) {
- warn "Estimating taxes on fee.\n";
+ warn "Estimating taxes on fee.\n" if $DEBUG;
# then we need to estimate tax to respect the maximum
# XXX currently doesn't work with external (tax_rate) taxes
# or batch taxes, obviously
if ($total_rate > 0) {
my $max_cents = $maximum * 100;
my $charge_cents = sprintf('%0.f', $max_cents * 100/(100 + $total_rate));
+ # the actual maximum that we can charge...
$maximum = sprintf('%.2f', $charge_cents / 100.00);
$amount = $maximum if $amount > $maximum;
}
'minimum' => 'Minimum fee',
'maximum' => 'Maximum fee',
'limit_credit' => 'Limit to customer credit balance',
+ 'nextbill' => 'Hold until the customer\'s next invoice',
%locale_labels
},
'fields' => \@fields,
value => 'Y',
},
+ { field => 'nextbill',
+ type => 'checkbox',
+ value => 'Y',
+ },
+
{ field => 'setuprecur',
type => 'select',
options => [ 'setup', 'recur' ],