diff options
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/API.pm | 5 | ||||
-rw-r--r-- | FS/FS/Password_Mixin.pm | 20 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 4 | ||||
-rw-r--r-- | FS/FS/cdr/FS/FS/cdr/vvs.pm | 7 | ||||
-rw-r--r-- | FS/FS/cdr/vss.pm | 33 | ||||
-rw-r--r-- | FS/FS/cust_bill_pkg.pm | 29 | ||||
-rw-r--r-- | FS/FS/cust_main/Billing.pm | 11 | ||||
-rw-r--r-- | FS/FS/cust_main/Billing_Realtime.pm | 2 | ||||
-rw-r--r-- | FS/FS/part_pkg/sql_external.pm | 71 |
9 files changed, 130 insertions, 52 deletions
diff --git a/FS/FS/API.pm b/FS/FS/API.pm index 4e6cb6cd7..4ff1a3ab5 100644 --- a/FS/FS/API.pm +++ b/FS/FS/API.pm @@ -65,6 +65,10 @@ Amount paid Option date for payment +=item order_number + +Optional order number + =back Example: @@ -77,6 +81,7 @@ Example: #optional '_date' => 1397977200, #UNIX timestamp + 'order_number' => '12345', ); if ( $result->{'error'} ) { diff --git a/FS/FS/Password_Mixin.pm b/FS/FS/Password_Mixin.pm index b80708116..2e400ec9d 100644 --- a/FS/FS/Password_Mixin.pm +++ b/FS/FS/Password_Mixin.pm @@ -60,7 +60,7 @@ sub is_password_allowed { # basic checks using Data::Password; # options for Data::Password - $DICTIONARY = 4; # minimum length of disallowed words + $DICTIONARY = 0; # minimum length of disallowed words, false value disables dictionary checking $MINLEN = $conf->config('passwordmin') || 6; $MAXLEN = $conf->config('passwordmax') || 8; $GROUPS = 4; # must have all 4 'character groups': numbers, symbols, uppercase, lowercase @@ -70,9 +70,23 @@ sub is_password_allowed { # # lists of disallowed words # @DICTIONARIES = qw( /usr/share/dict/web2 /usr/share/dict/words /usr/share/dict/linux.words ); + # first, no dictionary checking but require 4 char groups my $error = IsBadPassword($password); - $error = 'must contain at least one each of numbers, symbols, and lowercase and uppercase letters' - if $error eq 'contains less than 4 character groups'; # avoid confusion + + # but they can get away with 3 char groups, so long as they're not using a word + if ($error eq 'contains less than 4 character groups') { + $DICTIONARY = 4; # default from Data::Password is 5 + $GROUPS = 3; + $error = IsBadPassword($password); + # take note--we never actually report dictionary word errors; + # 4 char groups is the rule, 3 char groups and no dictionary words is an acceptable exception + $error = 'should contain at least one each of numbers, symbols, lowercase and uppercase letters' + if $error; + } + + # maybe also at some point add an exception for any passwords of sufficient length, + # see https://xkcd.com/936/ + $error = 'Invalid password - ' . $error if $error; return $error if $error; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index c40f6c747..c8f8c815e 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1774,7 +1774,7 @@ sub tables_hashref { 'gatewaynum', 'int', 'NULL', '', '', '', # payment_gateway FK 'processor', 'varchar', 'NULL', $char_d, '', '', # module name 'auth', 'varchar','NULL',16, '', '', # CC auth number - 'order_number','varchar','NULL',$char_d, '', '', # transaction number + 'order_number','varchar','NULL',256, '', '', # transaction number ], 'primary_key' => 'paynum', #i guess not now, with cust_pay_pending, if we actually make it here, we _do_ want to record it# 'unique' => [ [ 'payunique' ] ], @@ -1809,7 +1809,7 @@ sub tables_hashref { 'gatewaynum', 'int', 'NULL', '', '', '', # payment_gateway FK 'processor', 'varchar', 'NULL', $char_d, '', '', # module name 'auth', 'varchar','NULL',16, '', '', # CC auth number - 'order_number', 'varchar','NULL',$char_d, '', '', # transaction number + 'order_number', 'varchar','NULL',256, '', '', # transaction number #void fields 'void_date', @date_type, '', '', diff --git a/FS/FS/cdr/FS/FS/cdr/vvs.pm b/FS/FS/cdr/FS/FS/cdr/vvs.pm index 63a647ee8..db7e72ac6 100644 --- a/FS/FS/cdr/FS/FS/cdr/vvs.pm +++ b/FS/FS/cdr/FS/FS/cdr/vvs.pm @@ -18,12 +18,11 @@ use FS::cdr qw(_cdr_date_parser_maker); 'src', # caller 'dst', # called skip(2), # reason - # call id + # call id _cdr_date_parser_maker('startdate'), # time 'billsec', # duration - skip(3), # ringtime - # status - # resller_charge + skip(2), # ringtime + # reseller_charge 'upstream_price',# customer_charge ], ); diff --git a/FS/FS/cdr/vss.pm b/FS/FS/cdr/vss.pm deleted file mode 100644 index a550303df..000000000 --- a/FS/FS/cdr/vss.pm +++ /dev/null @@ -1,33 +0,0 @@ -package FS::cdr::vss; - -use strict; -use vars qw( @ISA %info $tmp_mon $tmp_mday $tmp_year ); -use Time::Local; -use FS::cdr qw(_cdr_date_parser_maker); - -@ISA = qw(FS::cdr); - -%info = ( - 'name' => 'VSS', - 'weight' => 120, - 'header' => 1, - 'import_fields' => [ - - skip(1), # i_customer - 'accountcode', # account_id - 'src', # caller - 'dst', # called - skip(2), # reason - # call id - _cdr_date_parser_maker('startdate'), # time - 'billsec', # duration - skip(3), # ringtime - # status - # resller_charge - 'upstream_price',# customer_charge - ], -); - -sub skip { map {''} (1..$_[0]) } - -1; diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 4448da62e..a15d7a260 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -1102,17 +1102,34 @@ sub cust_bill_pkg_tax_Xlocation { =item recur_show_zero -=cut +Whether to show a zero recurring amount. This is true if the package or its +definition has the recur_show_zero flag, and the recurring fee is actually +zero for this period. -sub recur_show_zero { shift->_X_show_zero('recur'); } -sub setup_show_zero { shift->_X_show_zero('setup'); } +=cut -sub _X_show_zero { +sub recur_show_zero { my( $self, $what ) = @_; - return 0 unless $self->$what() == 0 && $self->pkgnum; + return 0 unless $self->get('recur') == 0 && $self->pkgnum; + + $self->cust_pkg->_X_show_zero('recur'); +} + +=item setup_show_zero - $self->cust_pkg->_X_show_zero($what); +Whether to show a zero setup charge. This requires the package or its +definition to have the setup_show_zero flag, but it also returns false if +the package's setup date is before this line item's start date. + +=cut + +sub setup_show_zero { + my $self = shift; + return 0 unless $self->get('setup') == 0 && $self->pkgnum; + my $cust_pkg = $self->cust_pkg; + return 0 if ( $self->sdate || 0 ) > ( $cust_pkg->setup || 0 ); + return $cust_pkg->_X_show_zero('setup'); } =item credited [ BEFORE, AFTER, OPTIONS ] diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm index 2f9eecdf5..77529562f 100644 --- a/FS/FS/cust_main/Billing.pm +++ b/FS/FS/cust_main/Billing.pm @@ -1261,6 +1261,9 @@ sub _make_lines { my $unitrecur = 0; my @recur_discounts = (); my $sdate; + + my $override_quantity; + # Conditions for billing the recurring fee: # - the package doesn't have a future start date # - and it's not suspended @@ -1356,6 +1359,10 @@ sub _make_lines { #base_cancel??? $unitrecur = $cust_pkg->base_recur( \$sdate ) || $recur; #XXX uuh, better + if ( $param{'override_quantity'} ) { + $override_quantity = $param{'override_quantity'}; + } + if ( $increment_next_bill ) { my $next_bill; @@ -1410,7 +1417,7 @@ sub _make_lines { } } - } + } # end of recurring fee warn "\$setup is undefined" unless defined($setup); warn "\$recur is undefined" unless defined($recur); @@ -1477,7 +1484,7 @@ sub _make_lines { 'unitsetup' => sprintf('%.2f', $unitsetup), 'recur' => $recur, 'unitrecur' => sprintf('%.2f', $unitrecur), - 'quantity' => $cust_pkg->quantity, + 'quantity' => $override_quantity || $cust_pkg->quantity, 'details' => \@details, 'discounts' => [ @setup_discounts, @recur_discounts ], 'hidden' => $part_pkg->hidden, diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index 6c0b655a2..2a9e86924 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -522,6 +522,8 @@ sub realtime_bop { ? uc($options{'paytype'}) : uc($self->getfield('paytype')) || 'PERSONAL CHECKING'; + $content{company} = $self->company if $self->company; + if ( $content{account_type} =~ /BUSINESS/i && $self->company ) { $content{account_name} = $self->company; } else { diff --git a/FS/FS/part_pkg/sql_external.pm b/FS/FS/part_pkg/sql_external.pm index 813e8085c..48d89a0e3 100644 --- a/FS/FS/part_pkg/sql_external.pm +++ b/FS/FS/part_pkg/sql_external.pm @@ -6,6 +6,14 @@ use vars qw( %info ); use DBI; #use FS::Record qw(qsearch qsearchs); +tie our %query_style, 'Tie::IxHash', ( + 'simple' => 'Simple (a single value for the recurring charge)', + 'detailed' => 'Detailed (multiple rows for invoice details)', +); + +our @detail_cols = ( qw(amount format duration phonenum accountcode + startdate regionname detail) + ); %info = ( 'name' => 'Base charge plus additional fees for external services from a configurable SQL query', 'shortname' => 'External SQL query', @@ -34,10 +42,17 @@ use DBI; 'query' => { 'name' => 'SQL query', 'default' => '', }, + + 'query_style' => { + 'name' => 'Query output style', + 'type' => 'select', + 'select_options' => \%query_style, + }, + }, 'fieldorder' => [qw( recur_method cutoff_day ), FS::part_pkg::prorate_Mixin::fieldorder, - qw( datasrc db_username db_password query + qw( datasrc db_username db_password query query_style )], 'weight' => '58', ); @@ -53,6 +68,7 @@ sub calc_recur { my $self = shift; my($cust_pkg, $sdate, $details, $param ) = @_; my $price = 0; + my $quantity; # can be overridden; if not we use the default my $dbh = DBI->connect( map { $self->option($_) } qw( datasrc db_username db_password ) @@ -67,9 +83,60 @@ sub calc_recur { ) { my $id = $cust_svc->svc_x->id; $sth->execute($id) or die $sth->errstr; - $price += $sth->fetchrow_arrayref->[0]; + + if ( $self->option('query_style') eq 'detailed' ) { + + while (my $row = $sth->fetchrow_hashref) { + if (exists $row->{amount}) { + if ( $row->{amount} eq '' ) { + # treat as zero + } elsif ( $row->{amount} =~ /^\d+(?:\.\d+)?$/ ) { + $price += $row->{amount}; + } else { + die "sql_external query returned non-numeric amount: $row->{amount}"; + } + } + if (exists $row->{quantity}) { + $quantity ||= 0; + if ( $row->{quantity} eq '' ) { + # treat as zero + } elsif ( $row->{quantity} =~ /^\d+$/ ) { + $quantity += $row->{quantity}; + } else { + die "sql_external query returned non-integer quantity: $row->{quantity}"; + } + } + + my $detail = FS::cust_bill_pkg_detail->new; + foreach my $field (@detail_cols) { + if (exists $row->{$field}) { + $detail->set($field, $row->{$field}); + } + } + if (!$detail->get('detail')) { + die "sql_external query did not return detail description"; + # or make something up? + # or just don't insert the detail? + } + + push @$details, $detail; + } # while $row + + } else { + + # simple style: returns only a single value, which is the price + $price += $sth->fetchrow_arrayref->[0]; + + } + } + $price = sprintf('%.2f', $price); + + # XXX probably shouldn't allow package quantity > 1 on these packages. + if ($cust_pkg->quantity > 1) { + warn "sql_external package #".$cust_pkg->pkgnum." has quantity > 1\n"; } + $param->{'override_quantity'} = $quantity; $param->{'override_charges'} = $price; ($cust_pkg->quantity || 1) * $self->calc_recur_Common($cust_pkg,$sdate,$details,$param); } |