From: Christopher Burger Date: Tue, 13 Feb 2018 14:04:28 +0000 (-0500) Subject: Merge branch 'master' of ssh://git.freeside.biz/home/git/freeside X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=908b5627cc5899b00d3b9e83602403eb956dd038;hp=bd115bbb7ee8b1f8568d026caceb95ae18c80e87 Merge branch 'master' of ssh://git.freeside.biz/home/git/freeside --- diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 65eadad21..edecb7f38 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -5661,9 +5661,6 @@ sub tables_hashref { 'sessionnum', 'int', 'NULL', '', '', '', 'subscriber', 'varchar', 'NULL', $char_d, '', '', - #old - 'cdrbatch', 'varchar', 'NULL', 255, '', '', - #new 'cdrbatchnum', 'int', 'NULL', '', '', '', # FK to cust_bill_pkg_detail; having a value here absolutely means diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm index b9f3e9274..88fd4e87f 100644 --- a/FS/FS/Template_Mixin.pm +++ b/FS/FS/Template_Mixin.pm @@ -657,10 +657,11 @@ sub print_generic { $invoice_data{'cid'} = $params{'cid'} if $params{'cid'}; - if ( $cust_main->country eq $countrydefault ) { - $invoice_data{'country'} = ''; - } else { + if ( $cust_main->bill_locationnum + && $cust_main->bill_location->country ne $countrydefault ) { $invoice_data{'country'} = &$escape_function($cust_main->bill_country_full); + } else { + $invoice_data{'country'} = ''; } my @address = (); diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index 331ac0f2f..3de022466 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -170,7 +170,7 @@ following fields are currently supported: =item freesiderewritestatus - NULL, done, skipped -=item cdrbatch +=item cdrbatchnum =item detailnum - Link to invoice detail (L) @@ -240,7 +240,6 @@ sub table_info { 'svcnum' => 'Freeside service', 'freesidestatus' => 'Freeside status', 'freesiderewritestatus' => 'Freeside rewrite status', - 'cdrbatch' => 'Legacy batch', 'cdrbatchnum' => 'Batch', 'detailnum' => 'Freeside invoice detail line', }, @@ -1659,7 +1658,12 @@ foreach my $INC ( @INC ) { tie my %import_formats, 'Tie::IxHash', map { $_ => $cdr_info{$_}->{'name'} } - sort { $cdr_info{$a}->{'weight'} <=> $cdr_info{$b}->{'weight'} } + + #this is not doing anything useful anymore + #sort { $cdr_info{$a}->{'weight'} <=> $cdr_info{$b}->{'weight'} } + #so just sort alpha + sort { lc($cdr_info{$a}->{'name'}) cmp lc($cdr_info{$b}->{'name'}) } + grep { exists($cdr_info{$_}->{'import_fields'}) } keys %cdr_info; @@ -1868,41 +1872,6 @@ sub process_batch_import { # @columns = map { s/^ +//; $_; } @columns; # } -# _ upgrade_data -# -# Used by FS::Upgrade to migrate to a new database. - -sub _upgrade_data { - my ($class, %opts) = @_; - - warn "$me upgrading $class\n" if $DEBUG; - - my $sth = dbh->prepare( - 'SELECT DISTINCT(cdrbatch) FROM cdr WHERE cdrbatch IS NOT NULL' - ) or die dbh->errstr; - - $sth->execute or die $sth->errstr; - - my %cdrbatchnum = (); - while (my $row = $sth->fetchrow_arrayref) { - - my $cdr_batch = qsearchs( 'cdr_batch', { 'cdrbatch' => $row->[0] } ); - unless ( $cdr_batch ) { - $cdr_batch = new FS::cdr_batch { 'cdrbatch' => $row->[0] }; - my $error = $cdr_batch->insert; - die $error if $error; - } - - $cdrbatchnum{$row->[0]} = $cdr_batch->cdrbatchnum; - } - - $sth = dbh->prepare('UPDATE cdr SET cdrbatch = NULL, cdrbatchnum = ? WHERE cdrbatch IS NOT NULL AND cdrbatch = ?') or die dbh->errstr; - - foreach my $cdrbatch (keys %cdrbatchnum) { - $sth->execute($cdrbatchnum{$cdrbatch}, $cdrbatch) or die $sth->errstr; - } - -} =item ip_addr_sql FIELD RANGE diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 8b94dcc87..bd1b8bbec 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -215,7 +215,7 @@ sub insert { } -=item void [ REASON ] +=item void [ REASON [ , REPROCESS_CDRS ] ] Voids this invoice: deletes the invoice and adds a record of the voided invoice to the FS::cust_bill_void table (and related tables starting from @@ -226,6 +226,7 @@ FS::cust_bill_pkg_void). sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + my $reprocess_cdrs = scalar(@_) ? shift : ''; unless (ref($reason) || !$reason) { $reason = FS::reason->new_or_existing( @@ -257,7 +258,7 @@ sub void { } foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { - my $error = $cust_bill_pkg->void($reason); + my $error = $cust_bill_pkg->void($reason, $reprocess_cdrs); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -3541,6 +3542,23 @@ sub _items_aging_balances { return map{ sprintf('%.2f',$_) } @aging_balances; } +=item has_call_details + +Returns true if this invoice has call details. + +=cut + +sub has_call_details { + my $self = shift; + $self->scalar_sql(" + SELECT 1 FROM cust_bill_pkg_detail + LEFT JOIN cust_bill_pkg USING (billpkgnum) + WHERE cust_bill_pkg_detail.format = 'C' + AND cust_bill_pkg.invnum = ? + LIMIT 1 + ", $self->invnum); +} + =item call_details [ OPTION => VALUE ... ] Returns an array of CSV strings representing the call details for this invoice diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index e44a84709..77dce2476 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -324,7 +324,7 @@ sub insert { } -=item void [ REASON ] +=item void [ REASON [ , REPROCESS_CDRS ] ] Voids this line item: deletes the line item and adds a record of the voided line item to the FS::cust_bill_pkg_void table (and related tables). @@ -334,6 +334,7 @@ line item to the FS::cust_bill_pkg_void table (and related tables). sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + my $reprocess_cdrs = scalar(@_) ? shift : ''; unless (ref($reason) || !$reason) { $reason = FS::reason->new_or_existing( @@ -373,6 +374,9 @@ sub void { cust_tax_exempt_pkg cust_bill_pkg_fee )) { + my %delete_args = (); + $delete_args{'reprocess_cdrs'} = $reprocess_cdrs + if $table eq 'cust_bill_pkg_detail'; foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) { @@ -380,7 +384,7 @@ sub void { my $void = $vclass->new( { map { $_ => $linked->get($_) } $linked->fields }); - my $error = $void->insert || $linked->delete; + my $error = $void->insert || $linked->delete(%delete_args); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -863,7 +867,7 @@ sub _item_discount { # show introductory rate as a pseudo-discount if (!$d) { # this will conflict with showing real discounts my $part_pkg = $self->part_pkg; - if ( $part_pkg and $part_pkg->option('show_as_discount') ) { + if ( $part_pkg and $part_pkg->option('show_as_discount',1) ) { my $cust_pkg = $self->cust_pkg; my $intro_end = $part_pkg->intro_end($cust_pkg); my $_date = $self->cust_bill->_date; diff --git a/FS/FS/cust_bill_pkg_detail.pm b/FS/FS/cust_bill_pkg_detail.pm index dd118c1b2..19b15f7dd 100644 --- a/FS/FS/cust_bill_pkg_detail.pm +++ b/FS/FS/cust_bill_pkg_detail.pm @@ -106,21 +106,37 @@ sub insert { ''; } -=item delete +=item delete [ ARG => VALUE ... ] Delete this record from the database. +If the "reprocess_cdrs" argument is set to true, resets the status of any +related CDRs (and deletes their associated cdr_termination records, if any). + =cut sub delete { - my $self = shift; + my( $self, %args ) = @_; + my $error = $self->SUPER::delete; return $error if $error; + foreach my $cdr (qsearch('cdr', { detailnum => $self->detailnum })) { + $cdr->set('detailnum', ''); + $cdr->set('freesidestatus', '') if $args{'reprocess_cdrs'}; $error = $cdr->replace; return "error unlinking CDR #" . $cdr->acctid . ": $error" if $error; + + #well, technically this could have been on other invoices / termination + # partners... separate flag? + $self->scalar_sql( 'DELETE FROM cdr_termination WHERE acctid = ?', + $cdr->acctid ) + if $args{'reprocess_cdrs'}; + } + + ''; } =item replace OLD_RECORD diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 7d683235b..f11beec7d 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -6,7 +6,7 @@ use base qw( FS::cust_pkg::Search FS::cust_pkg::API ); use strict; -use Carp qw(cluck); +use Carp qw(cluck croak); use Scalar::Util qw( blessed ); use List::Util qw(min max sum); use Tie::IxHash; @@ -2372,8 +2372,20 @@ sub change { $same_pkgpart = 0; } - if ($opt->{'waive_setup'}) { $self->set('waive_setup', $opt->{'waive_setup'}) } - else { $self->set('waive_setup', ''); } + # Discounts: + # When a new discount level is specified in $opt: + # If new discountnum matches old discountnum, months_used/end_date are + # carried over as the discount is applied to the new cust_pkg + # + # Legacy behavior: + # Unless discount-related fields have been set within $opt, change() + # sets no discounts on the changed packages unless the new pkgpart is the + # same as the old pkgpart. In that case, discounts from the old cust_pkg + # are copied onto the new cust_pkg + + # Read discount fields from $opt + my %new_discount = $self->_parse_new_discounts($opt); + $self->set(waive_setup => $opt->{waive_setup} ? $opt->{waive_setup} : ''); # Before going any further here: if the package is still in the pre-setup # state, it's safe to modify it in place. No need to charge/credit for @@ -2429,6 +2441,22 @@ sub change { } # done transferring services + # Set waive_setup as directed + if ( !$error && exists $opt->{waive_setup} ) { + $self->set(waive_setup => $opt->{waive_setup}); + $error = $self->replace; + } + + # Set discounts if explicitly specified in $opt + if ( !$error && %new_discount ) { + $error = $self->change_discount(%new_discount); + } + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + $dbh->commit if $oldAutoCommit; return $self; @@ -2618,8 +2646,18 @@ sub change { } } - # transfer discounts, if we're not changing pkgpart - if ( $same_pkgpart ) { + if (%new_discount && !$error) { + + # If discounts were explicitly specified in $opt + $error = $cust_pkg->change_discount(%new_discount); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "applying discounts: $error"; + } + + } elsif ( $same_pkgpart ) { + + # transfer discounts, if we're not changing pkgpart foreach my $old_discount ($self->cust_pkg_discount_active) { # don't remove the old discount, we may still need to bill that package. my $new_discount = new FS::cust_pkg_discount { @@ -2836,6 +2874,20 @@ sub change_later { $opt->{'locationnum'} = $opt->{'cust_location'}->locationnum; } + # Discounts: + # Applies discounts to the newly created future_change package + # + # If a new discount is the same as the old discount, carry over the + # old discount's months_used/end_date fields too + # + # Legacy behavior: + # Legacy behavior was to create the next package with no discount. + # This behavior is preserved. Without the discount fields in $opt, + # the new package will be created with no discounts. + + # parse discount information from $opt + my %new_discount = $self->_parse_new_discounts($opt); + if ( $self->change_to_pkgnum ) { my $change_to = FS::cust_pkg->by_key($self->change_to_pkgnum); my $new_pkgpart = $opt->{'pkgpart'} @@ -2873,6 +2925,16 @@ sub change_later { $change_to->set('start_date', $date); $error = $self->replace || $change_to->replace; } + + if ( !$error && exists $opt->{waive_setup} ) { + $change_to->set(waive_setup => $opt->{waive_setup} ); + $error = $change_to->insert(); + } + + if ( !$error && %new_discount ) { + $error = $change_to->change_discount(%new_discount); + } + if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -2906,11 +2968,17 @@ sub change_later { } ); $error = $new->insert('change' => 1, 'allow_pkgpart' => ($new_pkgpart ? 0 : 1)); + + if ( !$error && %new_discount ) { + $error = $new->change_discount(%new_discount); + } + if ( !$error ) { $self->set('change_to_pkgnum', $new->pkgnum); $self->set('expire', $date); $error = $self->replace; } + if ( $error ) { $dbh->rollback if $oldAutoCommit; } else { @@ -2920,6 +2988,66 @@ sub change_later { $error; } +# Helper method reads $opt hashref from change() and change_later() +# Returns a hash of %new_discount suitable for passing to change_discount() +sub _parse_new_discounts { + my ($self, $opt) = @_; + + croak "Bad parameter list" unless ref $opt; + + my %old_discount = + map { $_->setuprecur => $_ } + qsearch('cust_pkg_discount', { + pkgnum => $self->pkgnum, + disabled => '', + }); + + my %new_discount; + for my $type(qw|setup recur|) { + + if (exists $opt->{"${type}_discountnum"}) { + $new_discount{$type} = { + discountnum => $opt->{"${type}_discountnum"}, + amount => $opt->{"${type}_discountnum_amount"}, + percent => $opt->{"${type}_discountnum_percent"}, + }; + } + + # Specified discountnum same as old discountnum, carry over addl fields + if ( + exists $opt->{"${type}_discountnum"} + && exists $old_discount{$type} + && $opt->{"${type}_discountnum"} eq $old_discount{$type}->discountnum + ){ + $new_discount{$type}->{months} = $old_discount{$type}->months; + $new_discount{$type}->{end_date} = $old_discount{$type}->end_date; + } + + # No new discount specified, carryover old discount + # If we wanted to abandon legacy behavior, and always carry old discounts + # uncomment this: + + # if (!exists $new_discount{$type} && $old_discount{$type}) { + # $new_discount{$type} = { + # discountnum => $old_discount{$type}->discountnum, + # amount => $old_discount{$type}->amount, + # percent => $old_discount{$type}->percent, + # months => $old_discount{$type}->months, + # end_date => $old_discount{$type}->end_date, + # }; + # } + } + + if ($DEBUG) { + warn "_parse_new_discounts(), pkgnum: ".$self->pkgnum." \n"; + warn "Determine \%old_discount, \%new_discount: \n"; + warn Dumper(\%old_discount); + warn Dumper(\%new_discount); + } + + %new_discount; +} + =item abort_change Cancels a future package change scheduled by C. @@ -4731,6 +4859,149 @@ sub insert_discount { ''; } +=item change_discount %opt + +Method checks if the given values represent a change in either setup or +discount level. If so, the existing discounts are revoked, the new +discounts are recorded. + +Usage: + +$error = change_discount( + setup => { + + # -1: Indicates a "custom discount" + # 0: Indicates to remove any discount + # >0: discountnum to apply + discountnum => [-1, 0, discountnum], + + # When discountnum is "-1" to indicate custom discount, include + # the additional fields: + amount => AMOUNT_DISCOUNT + percent => PERCENTAGE_DISCOUNT + months => -1, + }, + + recur => {...} +); + + +=cut + +sub change_discount { + my ($self, %opt) = @_; + return "change_discount() called with bad \%opt" + unless %opt; + + for (keys %opt) { + return "change_discount() called with unknown bad key $_" + unless $_ eq 'setup' || $_ eq 'recur'; + } + + my @old_discount = + qsearch('cust_pkg_discount',{ + pkgnum => $self->pkgnum, + disabled => '', + }); + + if ($DEBUG) { + warn "change_discount() pkgnum: ".$self->pkgnum." \n"; + warn "change_discount() \%opt: \n"; + warn Dumper(\%opt); + } + + my @to_be_disabled; + + for my $type (qw|setup recur|) { + next unless ref $opt{$type}; + my %change = %{$opt{$type}}; + + return "change_discount() called with bad \$opt($type)" + unless $change{discountnum} =~ /^-?\d+$/; + + if ($change{discountnum} eq 0) { + # Removing old discount + + delete $opt{$type}; + push @to_be_disabled, grep {$_->setuprecur eq $type} @old_discount; + } else { + + if ( + grep { + $_->discountnum eq $change{discountnum} + && $_->setuprecur eq $type + } @old_discount + ){ + # Duplicate, disregard this entry + delete $opt{$type}; + next; + } else { + # Mark any discounts we're replacing + push @to_be_disabled, grep{ $_->setuprecur eq $type} @old_discount; + } + + } + } + + + # If we still have changes queued, pass them to insert_discount() + # by setting values into object fields + for my $type (keys %opt) { + $self->set("${type}_discountnum", $opt{$type}->{discountnum}); + + if ($opt{$type}->{discountnum} eq '-1') { + $self->set("${type}_discountnum_${_}", $opt{$type}->{$_}) + for qw(amount percent months); + } + + } + + if ($DEBUG) { + warn "change_discount() \% opt before insert \n"; + warn Dumper \%opt; + warn "\@to_be_disabled \n"; + warn Dumper \@to_be_disabled; + } + + # Roll these updates into a transaction + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error; + + # The "waive setup fee" flag has traditionally been handled by setting + # $cust_pkg->waive_setup_fee = Y. This has been appropriately, and separately + # handled, and it operates on a differetnt table than cust_pkg_discount, + # so the "-2 for waive setup fee" option is not being reimplemented + # here. Perhaps this may change later. + # + # When a setup discount is entered, we still need unset waive_setup + if ( $opt{setup} && $opt{setup} > -2 && $self->waive_setup ) { + $self->set(waive_setup => ''); + $error = $self->replace(); + } + + # Create new discounts + $error ||= $self->insert_discount(); + + # Disabling old discounts + for my $tbd (@to_be_disabled) { + unless ($error) { + $tbd->set(disabled => 'Y'); + $error = $tbd->replace(); + } + } + + if ($error) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + $dbh->commit if $oldAutoCommit; + return undef; +} + =item set_usage USAGE_VALUE_HASHREF USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts @@ -5700,4 +5971,3 @@ L, schema.html from the base documentation =cut 1; - diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 956cf797c..da53715c6 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -232,6 +232,19 @@ sub insert { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + if ( length($self->classnum) && $self->classnum !~ /^(\d+)$/ ) { + my $pkg_class = qsearchs('pkg_class', { 'classname' => $self->classnum } ) + || new FS::pkg_class { classname => $self->classnum }; + unless ( $pkg_class->classnum ) { + my $error = $pkg_class->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + $self->classnum( $pkg_class->classnum ); + } + warn " inserting part_pkg record" if $DEBUG; my $error = $self->SUPER::insert( $options{options} ); if ( $error ) { diff --git a/FS/FS/part_pkg/prorate_Mixin.pm b/FS/FS/part_pkg/prorate_Mixin.pm index 1a7b1efc0..9e97cc593 100644 --- a/FS/FS/part_pkg/prorate_Mixin.pm +++ b/FS/FS/part_pkg/prorate_Mixin.pm @@ -205,7 +205,7 @@ sub prorate_setup { # For some reason (probably user override), the bill date has been set even # though the package isn't billing yet. Start billing as though that was the # start date. - $sdate = $cust_pkg->bill; + $$sdate = $cust_pkg->bill; $cust_pkg->setup($cust_pkg->bill); } # Now figure the start and end of the period that contains the start date. diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm index 6b0c9145f..a3f061291 100644 --- a/FS/FS/quotation.pm +++ b/FS/FS/quotation.pm @@ -716,7 +716,10 @@ sub estimate { my $cust_main; if ( $cust_or_prospect->isa('FS::prospect_main') ) { $cust_main = $cust_or_prospect->convert_cust_main; - die "$cust_main (simulating customer signup)\n" unless ref $cust_main; + unless ( ref($cust_main) ) { + $temp_dbh->rollback; + die "$cust_main (simulating customer signup)\n"; + } $fake_self->set('prospectnum', ''); $fake_self->set('custnum', $cust_main->custnum); } else { @@ -726,7 +729,10 @@ sub estimate { # order packages local($FS::cust_pkg::disable_start_on_hold) = 1; $error = $fake_self->order(\%pkgnum_of); - die "$error (simulating package order)\n" if $error; + if ( $error ) { + $temp_dbh->rollback; + die "$error (simulating package order)\n"; + } my @new_pkgs = map { FS::cust_pkg->by_key($_) } values(%pkgnum_of); @@ -739,7 +745,10 @@ sub estimate { 'no_usage_reset' => 1, ); $error = $cust_main->bill(%bill_opt); - die "$error (simulating initial billing)\n" if $error; + if ( $error ) { + $temp_dbh->rollback; + die "$error (simulating initial billing)\n" if $error; + } # pick dates for future bills my %next_bill_pkgs; @@ -755,7 +764,10 @@ sub estimate { $bill_opt{'return_bill'} = $return_bill[$i] = []; $bill_opt{'pkg_list'} = $next_bill_pkgs{$next_bill}; $error = $cust_main->bill(%bill_opt); - die "$error (simulating recurring billing cycle $i)\n" if $error; + if ( $error ) { + $temp_dbh->rollback; + die "$error (simulating recurring billing cycle $i)\n"; + } $i++; } diff --git a/FS/bin/freeside-passwd b/FS/bin/freeside-passwd new file mode 100755 index 000000000..dbd566e84 --- /dev/null +++ b/FS/bin/freeside-passwd @@ -0,0 +1,19 @@ +#!/usr/bin/perl -w + +use strict; + +my $user = shift or die &usage; +my $password = shift or die &usage; + +use FS::UID qw(adminsuidsetup); +use FS::Record qw( qsearchs ); +use FS::access_user; + +adminsuidsetup $user; + +my $access_user = qsearchs('access_user', {'username'=>$user}) + or die "unknown username $user\n"; +my $error = $access_user->change_password($password); +die $error if $error; + +1; diff --git a/bin/update-rates b/bin/update-rates new file mode 100644 index 000000000..b16fc7f78 --- /dev/null +++ b/bin/update-rates @@ -0,0 +1,42 @@ +#!/usr/bin/perl + +use FS::UID 'adminsuidsetup'; +use FS::Record qw(dbh qsearch qsearchs); +use FS::tax_class; +use FS::tax_rate; +use strict; + +adminsuidsetup('ivan'); +$FS::UID::AutoCommit = 0; +my @location = ( geocode => { op => 'like', value => '24%' } ); + +# convert TELECOMM RELAY SYSTEMS SURCHARGE:CENTREX LINES +# to TELECOMM RELAY SYSTEMS SURCHARGE:TELECOMMUNICATIONS +my $old_taxclassnum = qsearchs('tax_class', { 'taxclass' => '09:35' })->taxclassnum; +my $new_taxclassnum = qsearchs('tax_class', { 'taxclass' => '09:00' })->taxclassnum; +my $error; + +my @bindings = qsearch('part_pkg_taxrate', { + 'taxclassnum' => $old_taxclassnum, + @location +}); +print "remapping ".scalar(@bindings)." tax rate bindings.\n"; +foreach my $part_pkg_taxrate (@bindings) { + $part_pkg_taxrate->set('taxclassnum', $new_taxclassnum); + $error = $part_pkg_taxrate->replace; + die $part_pkg_taxrate->pkgtaxratenum .": $error" if $error; +} + +# change the fee to 0.05. +my @tax_rates = qsearch('tax_rate', { + taxclassnum => $new_taxclassnum, + @location +}); +print "changing rate on ".scalar(@tax_rates)." tax rate definitions.\n"; +foreach my $tax_rate (@tax_rates) { + $tax_rate->set('fee', 0.05); + my $error = $tax_rate->replace; + die $tax_rate->taxnum . ": $error\n" if $error; +} + +dbh->commit; diff --git a/debian/control b/debian/control index fc3bae1ce..2391f73f7 100644 --- a/debian/control +++ b/debian/control @@ -175,7 +175,7 @@ Description: Self-service portal html/cgi filesfor Freeside billing and trouble Package: freeside-ng-selfservice Architecture: all -Depends: libapache2-mod-php5,php5-xmlrpc,apache2 +Depends: libapache2-mod-php|libapache2-mod-php5,php5-xmlrpc,apache2 Recommends: Description: Next Generation Self-service portal for Freeside billing and trouble ticketing Freeside is a web-based billing and trouble ticketing application. diff --git a/debian/freeside-webui.postinst b/debian/freeside-webui.postinst new file mode 100644 index 000000000..8dd2baaf5 --- /dev/null +++ b/debian/freeside-webui.postinst @@ -0,0 +1,10 @@ +#!/bin/sh + +chown -R freeside /usr/local/etc/freeside +rm -fr /usr/local/etc/freeside/masondata/* + +#XXX systemd equivalent (start apache after postgres) +/sbin/insserv -d + +exit 0 + diff --git a/debian/freeside.postinst b/debian/freeside.postinst new file mode 100644 index 000000000..3e7dc2502 --- /dev/null +++ b/debian/freeside.postinst @@ -0,0 +1,10 @@ +#!/bin/sh + +#probably not needed with systemd +/usr/sbin/update-rc.d freeside defaults 23 01 + +chown -R freeside /usr/local/etc/freeside +rm -fr /usr/local/etc/freeside/masondata/* + +exit 0 + diff --git a/debian/postinst b/debian/postinst deleted file mode 100644 index 09f9daefd..000000000 --- a/debian/postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -chown -R freeside /usr/local/etc/freeside -/usr/sbin/update-rc.d freeside defaults 23 01 -/sbin/insserv -d -rm -fr /usr/local/etc/freeside/masondata/* - -exit 0 - diff --git a/debian/rules b/debian/rules index 1eb312ba4..041c895a4 100755 --- a/debian/rules +++ b/debian/rules @@ -228,8 +228,9 @@ install-stamp: build-stamp # Torrus - install -d ${TORRUS_CONF} - install -o root -m 755 htetc/freeside-torrus.conf $(TORRUS_CONF)/ + #in freeside-webui package + #install -d ${TORRUS_CONF} + #install -o root -m 755 htetc/freeside-torrus.conf $(TORRUS_CONF)/ ( cd torrus; \ torrus_user=freeside var_user=freeside var_group=freeside ./configure; \ diff --git a/httemplate/docs/about.html b/httemplate/docs/about.html index bc3c1b3b9..29a9fe0c4 100644 --- a/httemplate/docs/about.html +++ b/httemplate/docs/about.html @@ -28,7 +28,7 @@ % } else { % } -© 2017 Freeside Internet Services, Inc.
+© 2018 Freeside Internet Services, Inc.
All rights reserved.
Licensed under the terms of the
GNU Affero General Public License.
@@ -56,7 +56,7 @@ GNU Affero General Public License.
% unless ( $agentnum ) {
- "I've heard it too many times to ignore it / Its's something that I'm supposed to be" - K. Frog + "Then, when all seemed to be lost, a small glimmer of light appeared in the distance. Could it be... back from the great beyond..." - D. 3030
% } diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html index 9d2585366..f40575c0b 100644 --- a/httemplate/edit/access_user.html +++ b/httemplate/edit/access_user.html @@ -33,8 +33,10 @@ sub { my $access_user = shift; - '
Employee Groups
'. - ntable("#cccccc",2). + '
'. + 'Employee Groups'. + '
'. + ''. '
'. include( '/elements/checkboxes-table.html', 'source_obj' => $access_user, @@ -49,6 +51,7 @@ }, 'onsubmit' => 'check_user_custnum_search', 'html_foot' => $check_user_custnum_search, + 'html_table_class' => 'fsinnerbox', ) %> <%init> diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html index 54d2b0348..fcd210f83 100644 --- a/httemplate/edit/process/access_user.html +++ b/httemplate/edit/process/access_user.html @@ -11,7 +11,7 @@ 'target_table' => 'access_group', }, 'precheck_callback' => \&precheck_callback, - 'post_new_object_callback' => \&post_new_object_callback, + #'post_new_object_callback' => \&post_new_object_callback, 'noerror_callback' => \&noerror_callback, ) %> @@ -38,20 +38,24 @@ sub precheck_callback { return ''; } -sub post_new_object_callback { +#sub post_new_object_callback { +# my( $cgi, $access_user ) = @_; +# +# if ( length($cgi->param('_password')) ) { +# my $password = scalar($cgi->param('_password')); +# my $error = $access_user->is_password_allowed($password); +# #XXX and then bubble the error back up to the UI +# } +#} + +sub noerror_callback { my( $cgi, $access_user ) = @_; if ( length($cgi->param('_password')) ) { my $password = scalar($cgi->param('_password')); - my $error = $access_user->is_password_allowed($password) - || $access_user->change_password($password); + $access_user->change_password($password); } -} - -sub noerror_callback { - my( $cgi, $access_user ) = @_; - #handle installer checkbox my @sched_item = $access_user->sched_item; my $sched_item = $sched_item[0]; diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html index 7fcc1da07..02b01f8de 100644 --- a/httemplate/edit/process/change-cust_pkg.html +++ b/httemplate/edit/process/change-cust_pkg.html @@ -40,19 +40,54 @@ if ( $cgi->param('locationnum') == -1 ) { $change{'cust_location'} = $cust_location; } +my $error; + +# Discounts: +# setup_discountnum and change_discountnum may contain one of the following: +# - "-1" Represents the 'other' option. Results in a new entry to the +# discount table. +# - "-2" Represents the "waive setup fee" option. Sets cust_pkg.waive_setup = Y +# - int Represents the id for a discount row: discount.discountnum +# my %discount; +# $change{waive_setup} = ''; +# for my $type (qw|setup recur|) { +# my $dnum = $cgi->param("${type}_discountnum"); + +# if ($dnum eq '-2' && $type eq 'setup') { +# $change{waive_setup} = 'Y'; +# } elsif ($val =~ /^-?\d+$/) { +# $discount{$type} = {discountnum => $dnum}; +# if ($dnum eq '-1') { +# $discount{$type}->{amount} = $cgi->param("${type}_discountnum_amount"); +# $discount{$type}->{percent} = $cgi->param("${type}_discountnum_percent"); +# } +# } else { +# # Shouldn't happen without funny business +# $error = "Bad value ${type}_discountnum ($val)"; +# } +# } + + $change{waive_setup} = ''; +for my $type (qw|setup_discountnum recur_discountnum|) { + my $dnum = $cgi->param($type); -if ( $cgi->param('setup_discountnum') =~ /^(-?\d+)$/ ) { - if ( $1 == -2 ) { + if ($dnum eq '-2' && $type eq 'setup_discountnum') { + # Waive Discount $change{waive_setup} = 'Y'; + } elsif ($dnum =~ /^-?\d+$/) { + # Set discountnum + $change{$type} = $dnum; + $change{"${type}_amount"} = $cgi->param("${type}_amount"); + $change{"${type}_percent"} = $cgi->param("${type}_percent"); + } elsif ($dnum eq '') { + # Set discount as no discount + $change{"${type}"} = 0; } else { - $change{setup_discountnum} = $1; - $change{setup_discountnum_amount} = $cgi->param('setup_discountnum_amount'); - $change{setup_discountnum_percent} = $cgi->param('setup_discountnum_percent'); + $error = "Bad value ${type}_discountnum ($dnum)"; } } -my $error; my $now = time; if (defined($cgi->param('contract_end'))) { $change{'contract_end'} = parse_datetime($cgi->param('contract_end')); diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 9b8b2cd1e..eb065b668 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -518,8 +518,9 @@ tie my %tools_importing, 'Tie::IxHash', 'Customers' => [ $fsurl.'misc/cust_main-import.cgi', '' ], 'Package definitions' => [ $fsurl.'misc/part_pkg-import.html', '' ], 'Customer packages' => [ $fsurl.'misc/cust_pkg-import.html', '' ], +# 'Customer broadband services' => [ $fsurl.'misc/svc_broadband-import.html', '' ], 'Customer notes' => [ $fsurl.'misc/cust_main_note-import.html', '' ], - 'Customer Contacts' => [ $fsurl.'misc/contact-import.cgi', '' ], + 'Customer contacts' => [ $fsurl.'misc/contact-import.cgi', '' ], 'One-time charges' => [ $fsurl.'misc/cust_main-import_charges.cgi', '' ], 'Payments' => [ $fsurl.'misc/cust_pay-import.cgi', '' ], 'Credits' => [ $fsurl.'misc/cust_credit-import.html', '' ], diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi index 243da9308..2470ee135 100755 --- a/httemplate/misc/change_pkg.cgi +++ b/httemplate/misc/change_pkg.cgi @@ -90,7 +90,13 @@ % if ( $discount_cust_pkg || $waive_setup_fee ) { <% mt('Discounting') |h %> - <& /elements/tr-select-pkg-discount.html, disable_recur => 1, &> + <& /elements/tr-select-pkg-discount.html, + curr_value_setup => $discount{setup}, + curr_value_recur => $discount{recur}, + disable_setup => 0, + disable_recur => 0, + disable_waive_setup => 0 + &>

% } @@ -168,4 +174,14 @@ if ( $cust_pkg->change_to_pkgnum ) { } $title = "Edit Scheduled Package Change"; } + +# Get current values of discounts for selectboxes +my %discount = (setup => undef, recur => undef); +$discount{$_->setuprecur} = $_->discountnum + for qsearch('cust_pkg_discount', { + pkgnum => $cust_pkg->pkgnum, + disabled => '', + }); +$discount{setup} = '-2' if $cust_pkg->waive_setup; + diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html index 32a2fc591..49dda185e 100755 --- a/httemplate/misc/process/void-cust_bill.html +++ b/httemplate/misc/process/void-cust_bill.html @@ -19,8 +19,8 @@ my $invnum = $1; my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); -my $custnum = $cust_bill->custnum; - -my $error = $cust_bill->void( scalar($cgi->param('reason')) ); +my $error = $cust_bill->void( scalar($cgi->param('reason')), + scalar($cgi->param('reprocess_cdrs')), + ); diff --git a/httemplate/misc/void-cust_bill.html b/httemplate/misc/void-cust_bill.html index e4e4705d7..e5fbdc6d5 100755 --- a/httemplate/misc/void-cust_bill.html +++ b/httemplate/misc/void-cust_bill.html @@ -18,6 +18,14 @@ 'cgi' => $cgi &> +% if ( $cust_bill->has_call_details ) { + <& /elements/tr-checkbox.html, + label => 'Reprocess CDRs', + field => 'reprocess_cdrs', + value => '1', + &> +% } +

diff --git a/httemplate/view/prospect_main.html b/httemplate/view/prospect_main.html index 2fde797c6..f4dd4146f 100644 --- a/httemplate/view/prospect_main.html +++ b/httemplate/view/prospect_main.html @@ -108,8 +108,7 @@ my $prospect_main = qsearchs( { }); die "Prospect not found!" unless $prospect_main; -my $title = encode_entities($prospect_main->name); -$title = mt("Prospect"). ": $title"; +my $title = mt("Prospect"). ': '. $prospect_main->name; $title .= ' ('.mt('DISABLED').')' if $prospect_main->disabled;