From e151a538a611ed5aa3c08164cebc7230e5fa0da1 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Mon, 23 Feb 2015 19:17:29 -0800 Subject: [PATCH] option to force one-time charges to be billed separately, #32866 --- FS/FS/Schema.pm | 1 + FS/FS/cust_main.pm | 5 ++++- FS/FS/cust_main/Billing.pm | 28 ++++++++++++++++++------- FS/FS/cust_pkg.pm | 18 ++++++++++++---- httemplate/edit/process/quick-charge.cgi | 2 ++ httemplate/edit/quick-charge.html | 28 +++++++++++++++++-------- httemplate/view/cust_main/packages/package.html | 2 +- httemplate/view/cust_main/packages/status.html | 16 ++++++++++++++ 8 files changed, 77 insertions(+), 23 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index db2d3cf71..44f09d627 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -2659,6 +2659,7 @@ sub tables_hashref { 'recur_show_zero', 'char', 'NULL', 1, '', '', 'setup_show_zero', 'char', 'NULL', 1, '', '', 'change_to_pkgnum', 'int', 'NULL', '', '', '', + 'separate_bill', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'pkgnum', 'unique' => [], diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index c3b141e1b..7594fa852 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -3131,6 +3131,7 @@ sub charge { my ( $setuptax, $taxclass ); #internal taxes my ( $taxproduct, $override ); #vendor (CCH) taxes my $no_auto = ''; + my $separate_bill = ''; my $cust_pkg_ref = ''; my ( $bill_now, $invoice_terms ) = ( 0, '' ); my $locationnum; @@ -3153,7 +3154,8 @@ sub charge { $bill_now = exists($_[0]->{bill_now}) ? $_[0]->{bill_now} : ''; $invoice_terms = exists($_[0]->{invoice_terms}) ? $_[0]->{invoice_terms} : ''; $locationnum = $_[0]->{locationnum} || $self->ship_locationnum; - } else { + $separate_bill = $_[0]->{separate_bill} || ''; + } else { # yuck $amount = shift; $setup_cost = ''; $quantity = 1; @@ -3221,6 +3223,7 @@ sub charge { 'quantity' => $quantity, 'start_date' => $start_date, 'no_auto' => $no_auto, + 'separate_bill' => $separate_bill, 'locationnum'=> $locationnum, } ); diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm index b3d4e705f..9305eb33a 100644 --- a/FS/FS/cust_main/Billing.pm +++ b/FS/FS/cust_main/Billing.pm @@ -518,13 +518,31 @@ sub bill { push @{ $cust_bill_pkg{$pass} }, @transfer_items; # treating this as recur, just because most charges are recur... ${$total_recur{$pass}} += $_->recur foreach @transfer_items; + + # currently not considering separate_bill here, as it's for + # one-time charges only } foreach my $part_pkg ( @part_pkg ) { $cust_pkg->set($_, $hash{$_}) foreach qw ( setup last_bill bill ); - my $pass = ($cust_pkg->no_auto || $part_pkg->no_auto) ? 'no_auto' : ''; + my $pass = ''; + if ( $cust_pkg->separate_bill ) { + # if no_auto is also set, that's fine. we just need to not have + # invoices that are both auto and no_auto, and since the package + # gets an invoice all to itself, it will only be one or the other. + $pass = $cust_pkg->pkgnum; + if (!exists $cust_bill_pkg{$pass}) { # it may not exist yet + push @passes, $pass; + $total_setup{$pass} = do { my $z = 0; \$z }; + $total_recur{$pass} = do { my $z = 0; \$z }; + $taxlisthash{$pass} = {}; + $cust_bill_pkg{$pass} = []; + } + } elsif ( ($cust_pkg->no_auto || $part_pkg->no_auto) ) { + $pass = 'no_auto'; + } my $next_bill = $cust_pkg->getfield('bill') || 0; my $error; @@ -566,13 +584,7 @@ sub bill { } #foreach my $cust_pkg - #if the customer isn't on an automatic payby, everything can go on a single - #invoice anyway? - #if ( $cust_main->payby !~ /^(CARD|CHEK)$/ ) { - #merge everything into one list - #} - - foreach my $pass (@passes) { # keys %cust_bill_pkg ) { + foreach my $pass (@passes) { # keys %cust_bill_pkg ) my @cust_bill_pkg = _omit_zero_value_bundles(@{ $cust_bill_pkg{$pass} }); diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index b64d4dc38..ae86ca088 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -667,8 +667,9 @@ sub check { || $self->ut_numbern('resume') || $self->ut_numbern('expire') || $self->ut_numbern('dundate') - || $self->ut_enum('no_auto', [ '', 'Y' ]) - || $self->ut_enum('waive_setup', [ '', 'Y' ]) + || $self->ut_flag('no_auto', [ '', 'Y' ]) + || $self->ut_flag('waive_setup', [ '', 'Y' ]) + || $self->ut_flag('separate_bill') || $self->ut_textn('agent_pkgid') || $self->ut_enum('recur_show_zero', [ '', 'Y', 'N', ]) || $self->ut_enum('setup_show_zero', [ '', 'Y', 'N', ]) @@ -1065,7 +1066,8 @@ sub uncancel { setup susp adjourn resume expire start_date contract_end dundate change_date change_pkgpart change_locationnum - manual_flag no_auto quantity agent_pkgid recur_show_zero setup_show_zero + manual_flag no_auto separate_bill quantity agent_pkgid + recur_show_zero setup_show_zero ), }; @@ -2408,6 +2410,7 @@ and, I: - start_date: the date when it will be billed - amount: the setup fee to be charged - quantity: the multiplier for the setup fee +- separate_bill: whether to put the charge on a separate invoice If you pass 'adjust_commission' => 1, and the classnum changes, and there are commission credits linked to this charge, they will be recalculated. @@ -2463,7 +2466,8 @@ sub modify_charge { } if ( !$self->get('setup') ) { - # not yet billed, so allow amount, setup_cost, quantity and start_date + # not yet billed, so allow amount, setup_cost, quantity, start_date, + # and separate_bill if ( exists($opt{'amount'}) and $part_pkg->option('setup_fee') != $opt{'amount'} @@ -2493,6 +2497,12 @@ sub modify_charge { $self->set('start_date', $opt{'start_date'}); } + if ( exists($opt{'separate_bill'}) + and $opt{'separate_bill'} ne $self->separate_bill ) { + + $self->set('separate_bill', $opt{'separate_bill'}); + } + } # else simply ignore them; the UI shouldn't allow editing the fields diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi index aa6010ef9..c1e7fc159 100644 --- a/httemplate/edit/process/quick-charge.cgi +++ b/httemplate/edit/process/quick-charge.cgi @@ -93,6 +93,7 @@ if ( $param->{'pkgnum'} =~ /^(\d+)$/ ) { #modifying an existing one-time charge 'tax_override' => $override, 'quantity' => $quantity, 'start_date' => $start_date, + 'separate_bill' => scalar($cgi->param('separate_bill')), ); } else { # the usual case: new one-time charge @@ -138,6 +139,7 @@ if ( $param->{'pkgnum'} =~ /^(\d+)$/ ) { #modifying an existing one-time charge : '' ), 'no_auto' => scalar($cgi->param('no_auto')), + 'separate_bill' => scalar($cgi->param('separate_bill')), 'pkg' => scalar($cgi->param('pkg')), 'setuptax' => scalar($cgi->param('setuptax')), 'taxclass' => scalar($cgi->param('taxclass')), diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html index 58c1b0a82..78752c081 100644 --- a/httemplate/edit/quick-charge.html +++ b/httemplate/edit/quick-charge.html @@ -169,18 +169,22 @@ function bill_now_changed (what) { noinit => 1, } &> -% } -% unless ($billed) { - - <% mt('Tax exempt') |h %> - param('setuptax') ? 'CHECKED' : '' %>> - + <& /elements/tr-checkbox.html, + label => emt('Invoice this charge separately'), + field => 'separate_bill', + value => 'Y', + curr_value => $cust_pkg->get('separate_bill'), + &> + + <% mt('Tax exempt') |h %> + param('setuptax') ? 'CHECKED' : '' %>> + -<& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass') &> + <& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass') &> -<& /elements/tr-select-taxproduct.html, 'label' => emt('Tax product'), 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $part_pkg->get('taxproductnum') &> -% } + <& /elements/tr-select-taxproduct.html, 'label' => emt('Tax product'), 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $part_pkg->get('taxproductnum') &> +% } % } else { # new one-time charge @@ -280,6 +284,12 @@ function bill_now_changed (what) { }); +<& /elements/tr-checkbox.html, + label => emt('Invoice this charge separately'), + field => 'separate_bill', + value => 'Y' +&> + % } % if ( ! $quotationnum && $cust_main->payby =~ /^(CARD|CHEK)$/ ) { diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html index e47d891f5..3036f2e84 100644 --- a/httemplate/view/cust_main/packages/package.html +++ b/httemplate/view/cust_main/packages/package.html @@ -370,7 +370,7 @@ sub onetime_change_link { 'actionlabel' => emt('Modify'), 'cust_pkg' => $cust_pkg, 'width' => 690, - 'height' => 380, + 'height' => 440, ); } diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 36419646c..2bb4befb8 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -69,6 +69,8 @@ <% pkg_status_row_noauto( $cust_pkg, %opt ) %> + <% pkg_status_row_separate_bill( $cust_pkg, %opt ) %> + <% pkg_status_row_discount( $cust_pkg, %opt ) %> % unless ( $cust_pkg->order_date eq $cust_pkg->get('susp') ) { #on hold @@ -144,6 +146,8 @@ <% pkg_status_row_noauto( $cust_pkg, %opt ) %> + <% pkg_status_row_separate_bill( $cust_pkg, %opt ) %> + <% pkg_status_row_discount( $cust_pkg, %opt ) %> <% pkg_status_row_if( @@ -174,6 +178,8 @@ <% pkg_status_row_noauto( $cust_pkg, %opt ) %> + <% pkg_status_row_separate_bill( $cust_pkg, %opt ) %> + <% pkg_status_row_discount( $cust_pkg, %opt ) %> <% pkg_status_row_if($cust_pkg, emt('Start billing'), 'start_date', %opt) %> @@ -191,6 +197,8 @@ <% pkg_status_row_noauto( $cust_pkg, %opt ) %> + <% pkg_status_row_separate_bill( $cust_pkg, %opt ) %> + <% pkg_status_row_discount( $cust_pkg, %opt ) %> <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %> @@ -224,6 +232,8 @@ <% pkg_status_row_noauto( $cust_pkg, %opt ) %> + <% pkg_status_row_separate_bill( $cust_pkg, %opt ) %> + <% pkg_status_row_discount( $cust_pkg, %opt ) %> <% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt) %> @@ -496,6 +506,12 @@ sub pkg_status_row_noauto { pkg_status_row_colspan( $cust_pkg, emt("No automatic $what charge"), ''); } +sub pkg_status_row_separate_bill { + my $cust_pkg = shift; + return '' unless $cust_pkg->separate_bill; + pkg_status_row_colspan( $cust_pkg, emt("Invoiced separately") ); +} + sub pkg_status_row_discount { my( $cust_pkg, %opt ) = @_; -- 2.11.0