diff options
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS.pm | 2 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 21 | ||||
-rw-r--r-- | FS/FS/cust_credit.pm | 29 | ||||
-rw-r--r-- | FS/FS/cust_credit_source_bill_pkg.pm | 122 | ||||
-rw-r--r-- | FS/FS/cust_pkg.pm | 7 | ||||
-rw-r--r-- | FS/FS/part_export/a2billing.pm | 6 | ||||
-rw-r--r-- | FS/FS/part_pkg.pm | 106 | ||||
-rw-r--r-- | FS/FS/part_pkg/flat.pm | 25 | ||||
-rw-r--r-- | FS/FS/part_pkg/global_Mixin.pm | 45 | ||||
-rw-r--r-- | FS/MANIFEST | 2 | ||||
-rw-r--r-- | FS/t/cust_credit_source_bill_pkg.t | 5 |
11 files changed, 360 insertions, 10 deletions
@@ -452,6 +452,8 @@ L<FS::cust_credit_bill> - Credit application to invoice class L<FS::cust_credit_bill_pkg> - Line-item specific credit application to invoice class +L<FS::cust_credit_source_bill_pkg> - Line-item sources for triggered package credits + L<FS::cust_pay_refund> - Refund application to payment class L<FS::pay_batch> - Credit card transaction queue class diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index e854dc6ef..ca6d169e5 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1526,6 +1526,27 @@ sub tables_hashref { ], }, + 'cust_credit_source_bill_pkg' => { + 'columns' => [ + 'creditsourcebillpkgnum', 'serial', '', '', '', '', + 'crednum', 'int', '', '', '', '', + 'billpkgnum', 'int', '', '', '', '', + 'amount', @money_type, '', '', + 'currency', 'char', 'NULL', 3, '', '', + ], + 'primary_key' => 'creditsourcebillpkgnum', + 'unique' => [], + 'index' => [ ['crednum'], ['billpkgnum'] ], + 'foreign_keys' => [ + { columns => ['billpkgnum'], + table => 'cust_bill_pkg', + }, + { columns => ['crednum'], + table => 'cust_credit', + }, + ], + }, + 'cust_main' => { 'columns' => [ 'custnum', 'serial', '', '', '', '', diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm index 212be7a37..9eb624e48 100644 --- a/FS/FS/cust_credit.pm +++ b/FS/FS/cust_credit.pm @@ -132,11 +132,27 @@ sub cust_unlinked_msg { ' (cust_credit.crednum '. $self->crednum. ')'; } -=item insert +=item insert [ OPTION => VALUE ... ] Adds this credit to the database ("Posts" the credit). If there is an error, returns the error, otherwise returns false. +Ooptions are passed as a list of keys and values. Available options: + +=over 4 + +=item reason_type + +L<FS::reason_type|Reason> type for newly-inserted reason + +=item cust_credit_source_bill_pkg + +An arrayref of +L<FS::cust_credit_source_bill_pkg|FS::cust_credit_source_bilL_pkg> objects. +They will have their crednum set and will be inserted along with this credit. + +=back + =cut sub insert { @@ -176,6 +192,17 @@ sub insert { return "error inserting $self: $error"; } + if ( $options{'cust_credit_source_bill_pkg'} ) { + foreach my $ccsbr ( @{ $options{'cust_credit_source_bill_pkg'} } ) { + $ccsbr->crednum( $self->crednum ); + $error = $ccsbr->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "error inserting $ccsbr: $error"; + } + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; #false laziness w/ cust_pay::insert diff --git a/FS/FS/cust_credit_source_bill_pkg.pm b/FS/FS/cust_credit_source_bill_pkg.pm new file mode 100644 index 000000000..dae0d0f9b --- /dev/null +++ b/FS/FS/cust_credit_source_bill_pkg.pm @@ -0,0 +1,122 @@ +package FS::cust_credit_source_bill_pkg; +use base qw( FS::cust_main_Mixin FS::Record ); + +use strict; +#use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::cust_credit_source_bill_pkg - Object methods for cust_credit_source_bill_pkg records + +=head1 SYNOPSIS + + use FS::cust_credit_source_bill_pkg; + + $record = new FS::cust_credit_source_bill_pkg \%hash; + $record = new FS::cust_credit_source_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_source_bill_pkg object represents the record that a credit +was triggered by a specific line item. FS::cust_credit_source_bill_pkg +inherits from FS::Record. The following fields are currently supported: + +=over 4 + +=item creditsourcebillpkgnum + +Primary key + +=item crednum + +Credit (see L<FS::cust_credit>) + +=item billpkgnum + +Line item (see L<FS::cust_bill_pkg>) + +=item amount + +Amount specific to this line item. + +=item currency + +Currency + +=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 + +sub table { 'cust_credit_source_bill_pkg'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=item delete + +Delete this record from the database. + +=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. + +=item check + +Checks all fields to make sure this is a valid record. 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('creditsourcebillpkgnum') + || $self->ut_foreign_key('crednum', 'cust_credit', 'crednum') + || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum') + || $self->ut_money('amount') + || $self->ut_currencyn('currency') + ; + return $error if $error; + + $self->SUPER::check; +} + +=back + +=head1 BUGS + +Terminology/documentation surrounding credit "sources" vs. credit +"applications" is hard to understand. + +=head1 SEE ALSO + +L<FS::cust_credit>, L<FS::cust_bill_pkg>, L<FS::Record> + +=cut + +1; + diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index d55bd7bf0..5e070e38d 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -1440,7 +1440,11 @@ sub credit_remaining { and $next_bill > 0 # the package has a next bill date and $next_bill >= $time # which is in the future ) { - my $remaining_value = $self->calc_remain('time' => $time); + my @cust_credit_source_bill_pkg = (); + my $remaining_value = $self->calc_remain( + 'time' => $time, + 'cust_credit_source_bill_pkg' => \@cust_credit_source_bill_pkg, + ); if ( $remaining_value > 0 ) { warn "Crediting for $remaining_value on package ".$self->pkgnum."\n" if $DEBUG; @@ -1448,6 +1452,7 @@ sub credit_remaining { $remaining_value, 'Credit for unused time on '. $self->part_pkg->pkg, 'reason_type' => $reason_type, + 'cust_credit_source_bill_pkg' => \@cust_credit_source_bill_pkg, ); return "Error crediting customer \$$remaining_value for unused time". " on ". $self->part_pkg->pkg. ": $error" diff --git a/FS/FS/part_export/a2billing.pm b/FS/FS/part_export/a2billing.pm index b080d07b9..0821a34a0 100644 --- a/FS/FS/part_export/a2billing.pm +++ b/FS/FS/part_export/a2billing.pm @@ -227,6 +227,12 @@ sub export_insert { did => $svc->phonenum, billingtype => ($self->option('billtype') eq 'Dial Out Rate' ? 2 : 3), activated => 1, + aleg_carrier_cost_min_offp => $part_pkg->option('a2billing_carrier_cost_min'), + aleg_carrier_initblock_offp => $part_pkg->option('a2billing_carrier_initblock_offp'), + aleg_carrier_increment_offp => $part_pkg->option('a2billing_carrier_increment_offp'), + aleg_retail_cost_min_offp => $part_pkg->option('a2billing_retail_cost_min_offp'), + aleg_retail_initblock_offp => $part_pkg->option('a2billing_retail_initblock_offp'), + aleg_retail_increment_offp => $part_pkg->option('a2billing_retail_increment_offp'), ); # use 'did' as the key here so that if the DID already exists, we diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index d2f2f86fa..540919ee5 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -1638,16 +1638,120 @@ sub _rebless { $self; } +=item calc_setup CUST_PKG START_DATE DETAILS_ARRAYREF OPTIONS_HASHREF + +=item calc_recur CUST_PKG START_DATE DETAILS_ARRAYREF OPTIONS_HASHREF + +Calculates and returns the setup or recurring fees, respectively, for this +package. Implementation is in the FS::part_pkg:* module specific to this price +plan. + +Adds invoicing details to the passed-in DETAILS_ARRAYREF + +Options are passed as a hashref. Available options: + +=over 4 + +=item freq_override + +Frequency override (for calc_recur) + +=item discounts + +This option is filled in by the method rather than controlling its operation. +It is an arrayref. Applicable discounts will be added to the arrayref, as +L<FS::cust_bill_pkg_discount|FS::cust_bill_pkg_discount records>. + +=item real_pkgpart + +For package add-ons, is the base L<FS::part_pkg|package definition>, otherwise +no different than pkgpart. + +=item precommit_hooks + +This option is filled in by the method rather than controlling its operation. +It is an arrayref. Anonymous coderefs will be added to the arrayref. They +need to be called before completing the billing operation. For calc_recur +only. + +=item increment_next_bill + +Increment the next bill date (boolean, for calc_recur). Typically true except +for particular situations. + +=item setup_fee + +This option is filled in by the method rather than controlling its operation. +It indicates a deferred setup fee that is billed at calc_recur time (see price +plan option prorate_defer_bill). + +=back + +Note: Don't calculate prices when not actually billing the package. For that, +see the L</base_setup|base_setup> and L</base_recur|base_recur> methods. + +=cut + #fatal fallbacks sub calc_setup { die 'no calc_setup for '. shift->plan. "\n"; } sub calc_recur { die 'no calc_recur for '. shift->plan. "\n"; } -#fallback that return 0 for old legacy packages with no plan +=item calc_remain CUST_PKG [ OPTION => VALUE ... ] + +Calculates and returns the remaining value to be credited upon package +suspension, change, or cancellation, if enabled. + +Options are passed as a list of keys and values. Available options: + +=over 4 + +=item time + +Override for the current time + +=item cust_credit_source_bill_pkg + +This option is filled in by the method rather than controlling its operation. +It is an arrayref. +L<FS::cust_credit_source_bill_pkg|FS::cust_credit_source_bill_pkg> records will +be added to the arrayref indicating the specific line items and amounts which +are the source of this remaining credit. + +=back + +Note: Don't calculate prices when not actually suspending or cancelling the +package. + +=cut + +#fallback that returns 0 for old legacy packages with no plan sub calc_remain { 0; } + +=item calc_units CUST_PKG + +This returns the number of provisioned svc_phone records, or, of the package +count_available_phones option is set, the number available to be provisoined +in the package. + +=cut + +#fallback that returns 0 for old legacy packages with no plan sub calc_units { 0; } #fallback for everything not based on flat.pm sub recur_temporality { 'upcoming'; } + +=item calc_cancel START_DATE DETAILS_ARRAYREF OPTIONS_HASHREF + +Runs any necessary billing on cancellation: another recurring cycle for +recur_temporailty 'preceding' pacakges with the bill_recur_on_cancel option +set (calc_recur), or, any outstanding usage for pacakges with the +bill_usage_on_cancel option set (calc_usage). + +=cut + +#fallback for everything not based on flat.pm, doesn't do this yet (which is +#okay, nothing of ours not based on flat.pm does usage-on-cancel billing sub calc_cancel { 0; } #fallback for everything except bulk.pm diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm index cb2986efd..d9d458809 100644 --- a/FS/FS/part_pkg/flat.pm +++ b/FS/FS/part_pkg/flat.pm @@ -7,6 +7,7 @@ use base qw( FS::part_pkg::prorate_Mixin use strict; use vars qw( %info %usage_recharge_fields @usage_recharge_fieldorder ); use FS::Record qw( qsearch ); +use FS::cust_credit_source_bill_pkg; use Tie::IxHash; use List::Util qw( min ); use FS::UI::bytecount; @@ -242,7 +243,7 @@ sub calc_remain { # Use sdate < $time and edate >= $time because when billing on # cancellation, edate = $time. my $credit = 0; - foreach my $item ( + foreach my $cust_bill_pkg ( qsearch('cust_bill_pkg', { pkgnum => $cust_pkg->pkgnum, sdate => {op => '<' , value => $time}, @@ -250,16 +251,28 @@ sub calc_remain { recur => {op => '>' , value => 0}, }) ) { + # hack to deal with the weird behavior of edate on package cancellation - my $edate = $item->edate; + my $edate = $cust_bill_pkg->edate; if ( $self->recur_temporality eq 'preceding' ) { - $edate = $self->add_freq($item->sdate); + $edate = $self->add_freq($cust_bill_pkg->sdate); } - $credit += ($item->recur - $item->usage) * - ($edate - $time) / ($edate - $item->sdate); + + my $amount = ($cust_bill_pkg->recur - $cust_bill_pkg->usage) * + ($edate - $time) / ($edate - $cust_bill_pkg->sdate); + $credit += $amount; + + push @{ $options{'cust_credit_source_bill_pkg'} }, + new FS::cust_credit_source_bill_pkg { + 'billpkgnum' => $cust_bill_pkg->billpkgnum, + 'amount' => sprintf('%.2f', $amount), + 'currency' => $cust_bill_pkg->cust_bill->currency, + } + if $options{'cust_credit_source_bill_pkg'}; + } + sprintf('%.2f', $credit); - #sprintf("%.2f", $self->base_recur($cust_pkg, \$time) * ( $next_bill - $time ) / $freq_sec ); } diff --git a/FS/FS/part_pkg/global_Mixin.pm b/FS/FS/part_pkg/global_Mixin.pm index 899e73abc..263772955 100644 --- a/FS/FS/part_pkg/global_Mixin.pm +++ b/FS/FS/part_pkg/global_Mixin.pm @@ -65,7 +65,44 @@ tie my %a2billing_simultaccess, 'Tie::IxHash', ( 'type' => 'select', 'select_options' => \%a2billing_simultaccess, }, - }, + 'a2billing_carrier_cost_min' => { + 'name' => 'A2Billing inbound carrier cost', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + 'a2billing_carrer_initblock_offp' => { + 'name' => 'A2Billing inbound carrier min duration', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + 'a2billing_carrier_increment_offp' => { + 'name' => 'A2Billing inbound carrier billing block', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + 'a2billing_retail_cost_min_offp' => { + 'name' => 'A2Billing inbound retail cost', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + 'a2billing_retail_initblock_offp' => { + 'name' => 'A2Billing inbound retail min duration', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + 'a2billing_retail_increment_offp' => { + 'name' => 'A2Billing inbound retail billing block', + 'display_if' => sub { + FS::part_export->count("exporttype = 'a2billing'") > 0; + }, + }, + + }, 'fieldorder' => [ qw( setup_fee recur_fee @@ -76,6 +113,12 @@ tie my %a2billing_simultaccess, 'Tie::IxHash', ( a2billing_tariff a2billing_type a2billing_simultaccess + a2billing_carrier_cost_min + a2billing_carrer_initblock_offp + a2billing_carrier_increment_offp + a2billing_retail_cost_min_offp + a2billing_retail_initblock_offp + a2billing_retail_increment_offp )], ); diff --git a/FS/MANIFEST b/FS/MANIFEST index 581ab0d1f..6e36c3344 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -832,3 +832,5 @@ FS/circuit_termination.pm t/circuit_termination.t FS/svc_circuit.pm t/svc_circuit.t +FS/cust_credit_source_bill_pkg.pm +t/cust_credit_source_bill_pkg.t diff --git a/FS/t/cust_credit_source_bill_pkg.t b/FS/t/cust_credit_source_bill_pkg.t new file mode 100644 index 000000000..45da43370 --- /dev/null +++ b/FS/t/cust_credit_source_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_source_bill_pkg; +$loaded=1; +print "ok 1\n"; |