From d46254f9b36873e457424eefdcf3610b71ef889d Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Sat, 31 Jan 2015 15:28:52 -0800 Subject: [PATCH] fix sprintf error, mostly #31273 --- FS/FS/Schema.pm | 4 + FS/FS/TemplateItem_Mixin.pm | 10 +- FS/FS/Template_Mixin.pm | 152 +++++++++-------- FS/FS/cust_bill_pkg.pm | 39 +++++ FS/FS/cust_bill_pkg_discount.pm | 33 ++++ FS/FS/quotation.pm | 21 +++ FS/FS/quotation_pkg.pm | 253 +++++++++++++++++++++++++---- FS/FS/quotation_pkg_discount.pm | 49 +++++- httemplate/edit/process/quick-cust_pkg.cgi | 2 +- 9 files changed, 443 insertions(+), 120 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index ef381e573..6301df2da 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1336,6 +1336,8 @@ sub tables_hashref { 'contract_end', @date_type, '', '', 'quantity', 'int', 'NULL', '', '', '', 'waive_setup', 'char', 'NULL', 1, '', '', + 'unitsetup', @money_typen, '', '', + 'unitrecur', @money_typen, '', '', ], 'primary_key' => 'quotationpkgnum', 'unique' => [], @@ -1347,6 +1349,8 @@ sub tables_hashref { 'quotationpkgdiscountnum', 'serial', '', '', '', '', 'quotationpkgnum', 'int', '', '', '', '', 'discountnum', 'int', '', '', '', '', + 'setup_amount', @money_typen, '', '', + 'recur_amount', @money_typen, '', '', #'end_date', @date_type, '', '', ], 'primary_key' => 'quotationpkgdiscountnum', diff --git a/FS/FS/TemplateItem_Mixin.pm b/FS/FS/TemplateItem_Mixin.pm index 6ae3364d1..27b8f1bdb 100644 --- a/FS/FS/TemplateItem_Mixin.pm +++ b/FS/FS/TemplateItem_Mixin.pm @@ -367,15 +367,17 @@ sub cust_bill_pkg_detail { } -=item cust_bill_pkg_discount +=item pkg_discount -Returns the list of associated cust_bill_pkg_discount objects. +Returns the list of associated cust_bill_pkg_discount or +quotation_pkg_discount objects. =cut -sub cust_bill_pkg_discount { +sub pkg_discount { my $self = shift; - qsearch( $self->discount_table, { 'billpkgnum' => $self->billpkgnum } ); + my $pkey = $self->primary_key; + qsearch( $self->discount_table, { $pkey => $self->get($pkey) } ); } 1; diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm index 1fed7f1b9..986b308c4 100644 --- a/FS/FS/Template_Mixin.pm +++ b/FS/FS/Template_Mixin.pm @@ -691,11 +691,12 @@ sub print_generic { # (this is used in the summary & on the payment coupon) $invoice_data{'balance'} = sprintf("%.2f", $balance_due); - # info from customer's last invoice before this one, for some - # summary formats - $invoice_data{'last_bill'} = {}; + # flag telling this invoice to have a first-page summary + my $summarypage = ''; if ( $self->custnum && $self->invnum ) { + # XXX should be an FS::cust_bill method to set the defaults, instead + # of checking the type here my $last_bill = $self->previous_bill; if ( $last_bill ) { @@ -801,13 +802,16 @@ sub print_generic { $invoice_data{'previous_payments'} = []; $invoice_data{'previous_credits'} = []; } - } # if this is an invoice - my $summarypage = ''; - if ( $conf->exists('invoice_usesummary', $agentnum) ) { - $summarypage = 1; - } - $invoice_data{'summarypage'} = $summarypage; + # info from customer's last invoice before this one, for some + # summary formats + $invoice_data{'last_bill'} = {}; + + if ( $conf->exists('invoice_usesummary', $agentnum) ) { + $invoice_data{'summarypage'} = $summarypage = 1; + } + + } # if this is an invoice warn "$me substituting variables in notes, footer, smallfooter\n" if $DEBUG > 1; @@ -1172,6 +1176,12 @@ sub print_generic { join(', ', map "$_=>".$line_item->{$_}, keys %$line_item). "\n" if $DEBUG > 1; + push @buf, ( [ $line_item->{'description'}, + $money_char. sprintf("%10.2f", $line_item->{'amount'}), + ], + map { [ " ". $_, '' ] } @{$line_item->{'ext_description'}}, + ); + $line_item->{'ref'} = $line_item->{'pkgnum'}; $line_item->{'product_code'} = $line_item->{'pkgpart'} || 'N/A'; # mt()? $line_item->{'section'} = $section; @@ -1184,11 +1194,6 @@ sub print_generic { $line_item->{'ext_description'} ||= []; push @detail_items, $line_item; - push @buf, ( [ $line_item->{'description'}, - $money_char. sprintf("%10.2f", $line_item->{'amount'}), - ], - map { [ " ". $_, '' ] } @{$line_item->{'ext_description'}}, - ); } if ( $section->{'description'} ) { @@ -3092,7 +3097,9 @@ sub _items_cust_bill_pkg { if $cust_bill_pkg->recur != 0 || $discount_show_always || $cust_bill_pkg->recur_show_zero; - push @b, { + #push @b, { + # keep it consistent, please + $s = { 'pkgnum' => $cust_bill_pkg->pkgpart, #so it displays in Ref 'description' => $description, 'amount' => sprintf("%.2f", $cust_bill_pkg->setup), @@ -3105,7 +3112,8 @@ sub _items_cust_bill_pkg { }; } if ( $cust_bill_pkg->recur != 0 ) { - push @b, { + #push @b, { + $r = { 'pkgnum' => $cust_bill_pkg->pkgpart, #so it displays in Ref 'description' => "$desc (". $cust_bill_pkg->part_pkg->freq_pretty.")", 'amount' => sprintf("%.2f", $cust_bill_pkg->recur), @@ -3398,68 +3406,6 @@ sub _items_cust_bill_pkg { } # recurring or usage with recurring charge - # decide whether to show active discounts here - if ( - # case 1: we are showing a single line for the package - ( !$type ) - # case 2: we are showing a setup line for a package that has - # no base recurring fee - or ( $type eq 'S' and $cust_bill_pkg->unitrecur == 0 ) - # case 3: we are showing a recur line for a package that has - # a base recurring fee - or ( $type eq 'R' and $cust_bill_pkg->unitrecur > 0 ) - ) { - - # the line item hashref for the line that will show the original - # price - # (use the recur or single line for the package, unless we're - # showing a setup line for a package with no recurring fee) - my $active_line = $r; - if ( $type eq 'S' ) { - $active_line = $s; - } - - my @discounts = $cust_bill_pkg->cust_bill_pkg_discount; - # special case: if there are old "discount details" on this line - # item, don't show discount line items - if ( FS::cust_bill_pkg_detail->count( - "detail LIKE 'Includes discount%' AND billpkgnum = " . - $cust_bill_pkg->billpkgnum - ) > 0 ) { - @discounts = (); - } - if ( @discounts ) { - warn "$me _items_cust_bill_pkg including discounts for ". - $cust_bill_pkg->billpkgnum."\n" - if $DEBUG; - my $discount_amount = sum( map {$_->amount} @discounts ); - # if multiple discounts apply to the same package, how to display - # them? ext_description lines, apparently - # - # # discount amounts are negative - if ( $d and $cust_bill_pkg->hidden ) { - $d->{amount} -= $discount_amount; - } else { - my @ext; - $d = { - _is_discount => 1, - description => $self->mt('Discount'), - amount => -1 * $discount_amount, - ext_description => \@ext, - }; - foreach my $cust_bill_pkg_discount (@discounts) { - my $def = $cust_bill_pkg_discount->cust_pkg_discount->discount; - push @ext, &{$escape_function}( $def->description ); - } - } - - # update the active line (before the discount) to show the - # original price (whether this is a hidden line or not) - $active_line->{amount} += $discount_amount; - - } # if there are any discounts - } # if this is an appropriate place to show discounts - } else { # taxes and fees warn "$me _items_cust_bill_pkg cust_bill_pkg is tax\n" @@ -3474,6 +3420,56 @@ sub _items_cust_bill_pkg { } # if quotation / package line item / other line item + # decide whether to show active discounts here + if ( + # case 1: we are showing a single line for the package + ( !$type ) + # case 2: we are showing a setup line for a package that has + # no base recurring fee + or ( $type eq 'S' and $cust_bill_pkg->unitrecur == 0 ) + # case 3: we are showing a recur line for a package that has + # a base recurring fee + or ( $type eq 'R' and $cust_bill_pkg->unitrecur > 0 ) + ) { + + my $item_discount = $cust_bill_pkg->_item_discount; + if ( $item_discount ) { + # $item_discount->{amount} is negative + + if ( $d and $cust_bill_pkg->hidden ) { + $d->{amount} += $item_discount->{amount}; + } else { + $d = $item_discount; + $_ = &{$escape_function}($_) foreach @{ $d->{ext_description} }; + } + + # update the active line (before the discount) to show the + # original price (whether this is a hidden line or not) + # + # quotation discounts keep track of setup and recur; invoice + # discounts currently don't + if ( exists $item_discount->{setup_amount} ) { + + $s->{amount} -= $item_discount->{setup_amount} if $s; + $r->{amount} -= $item_discount->{recur_amount} if $r; + + } else { + + # $active_line is the line item hashref for the line that will + # show the original price + # (use the recur or single line for the package, unless we're + # showing a setup line for a package with no recurring fee) + my $active_line = $r; + if ( $type eq 'S' ) { + $active_line = $s; + } + $active_line->{amount} -= $item_discount->{amount}; + + } + + } # if there are any discounts + } # if this is an appropriate place to show discounts + } # foreach $display $discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index d87e13744..4718d1824 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -671,6 +671,45 @@ sub units { $self->pkgnum ? $self->part_pkg->calc_units($self->cust_pkg) : 0; # 1? } +=item _item_discount + +If this item has any discounts, returns a hashref in the format used +by L to describe the discount(s) +on an invoice. This will contain the keys 'description', 'amount', +'ext_description' (an arrayref of text lines describing the discounts), +and '_is_discount' (a flag). + +The value for 'amount' will be negative, and will be scaled for the package +quantity. + +=cut + +sub _item_discount { + my $self = shift; + my @pkg_discounts = $self->pkg_discount; + return if @pkg_discounts == 0; + # special case: if there are old "discount details" on this line item, don't + # show discount line items + if ( FS::cust_bill_pkg_detail->count("detail LIKE 'Includes discount%' AND billpkgnum = ?", $self->billpkgnum || 0) > 0 ) { + return; + } + + my @ext; + my $d = { + _is_discount => 1, + description => $self->mt('Discount'), + amount => 0, + ext_description => \@ext, + # maybe should show quantity/unit discount? + }; + foreach my $pkg_discount (@pkg_discounts) { + push @ext, $pkg_discount->description; + $d->{amount} -= $pkg_discount->amount; + } + $d->{amount} *= $self->quantity || 1; + + return $d; +} =item set_display OPTION => VALUE ... diff --git a/FS/FS/cust_bill_pkg_discount.pm b/FS/FS/cust_bill_pkg_discount.pm index dfa83d393..1d6866a32 100644 --- a/FS/FS/cust_bill_pkg_discount.pm +++ b/FS/FS/cust_bill_pkg_discount.pm @@ -144,6 +144,39 @@ sub cust_pkg_discount { } +=item description + +Returns a string describing the discount (for use on an invoice). + +=cut + +sub description { + my $self = shift; + my $discount = $self->cust_pkg_discount->discount; + my $desc = $discount->description_short; + $desc .= $self->mt(' each') if $self->cust_bill_pkg->quantity > 1; + + if ($discount->months) { + # calculate months remaining on this cust_pkg_discount after this invoice + my $date = $self->cust_bill_pkg->cust_bill->_date; + my $used = FS::Record->scalar_sql( + 'SELECT SUM(months) FROM cust_bill_pkg_discount + JOIN cust_bill_pkg USING (billpkgnum) + JOIN cust_bill USING (invnum) + WHERE pkgdiscountnum = ? AND _date <= ?', + $self->pkgdiscountnum, + $date + ); + $used ||= 0; + my $remaining = sprintf('%.2f', $discount->months - $used); + $desc .= $self->mt(' for [quant,_1,month] ([quant,_2,month] remaining)', + $self->months, + $remaining + ); + } + return $desc; +} + =back =head1 BUGS diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm index b3595efcf..a8cbfeb95 100644 --- a/FS/FS/quotation.pm +++ b/FS/FS/quotation.pm @@ -656,6 +656,27 @@ sub search_sql_where { } +=item _items_pkg + +Return line item hashes for each package on this quotation. Differs from the +base L version in that it recalculates each quoted package +first, and doesn't implement the "condensed" option. + +=cut + +sub _items_pkg { + my ($self, %options) = @_; + my @quotation_pkg = $self->quotation_pkg; + foreach (@quotation_pkg) { + my $error = $_->estimate; + die "error calculating estimate for pkgpart " . $_->pkgpart.": $error\n" + if $error; + } + + # run it through the Template_Mixin engine + return $self->_items_cust_bill_pkg(\@quotation_pkg, %options); +} + =back =head1 BUGS diff --git a/FS/FS/quotation_pkg.pm b/FS/FS/quotation_pkg.pm index cc45a85e9..28677d0dc 100644 --- a/FS/FS/quotation_pkg.pm +++ b/FS/FS/quotation_pkg.pm @@ -2,11 +2,12 @@ package FS::quotation_pkg; use strict; use base qw( FS::TemplateItem_Mixin FS::Record ); -use FS::Record qw( qsearchs ); #qsearch +use FS::Record qw( qsearchs dbh ); #qsearch use FS::part_pkg; use FS::cust_location; use FS::quotation; use FS::quotation_pkg_discount; #so its loaded when TemplateItem_Mixin needs it +use List::Util qw(sum); =head1 NAME @@ -41,19 +42,19 @@ primary key =item pkgpart -pkgpart +pkgpart (L) of the package =item locationnum -locationnum +locationnum (L) where the package will be in service =item start_date -start_date +expected start date for the package, as a timestamp =item contract_end -contract_end +contract end date =item quantity @@ -61,8 +62,15 @@ quantity =item waive_setup -waive_setup +'Y' to waive the setup fee +=item unitsetup + +The amount per package that will be charged in setup/one-time fees. + +=item unitrecur + +The amount per package that will be charged per billing cycle. =back @@ -95,10 +103,69 @@ sub discount_table { 'quotation_pkg_discount'; } Adds this record to the database. If there is an error, returns the error, otherwise returns false. +=cut + +sub insert { + my ($self, %options) = @_; + + my $dbh = dbh; + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + + my $error = $self->SUPER::insert; + + if ( !$error and $self->discountnum ) { + $error = $self->insert_discount; + $error .= ' (setting discount)' if $error; + } + + # update $self and any discounts with their amounts + if ( !$error ) { + $error = $self->estimate; + $error .= ' (calculating charges)' if $error; + } + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } else { + $dbh->commit if $oldAutoCommit; + return ''; + } +} + =item delete Delete this record from the database. +=cut + +sub delete { + my $self = shift; + + my $dbh = dbh; + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + + foreach ($self->quotation_pkg_discount) { + my $error = $_->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error . ' (deleting discount)'; + } + } + + my $error = $self->SUPER::delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } else { + $dbh->commit if $oldAutoCommit; + return ''; + } + +} + =item replace OLD_RECORD Replaces the OLD_RECORD with this one in the database. If there is an error, @@ -123,8 +190,11 @@ sub check { || $self->ut_numbern('start_date') || $self->ut_numbern('contract_end') || $self->ut_numbern('quantity') + || $self->ut_moneyn('unitsetup') + || $self->ut_moneyn('unitrecur') || $self->ut_enum('waive_setup', [ '', 'Y'] ) ; + return $error if $error; $self->SUPER::check; @@ -140,48 +210,159 @@ sub desc { $self->part_pkg->pkg; } -sub setup { +=item estimate + +Update the quotation_pkg record with the estimated setup and recurring +charges for the package. Returns nothing on success, or an error message +on failure. + +=cut + +sub estimate { my $self = shift; - return '0.00' if $self->waive_setup eq 'Y' || $self->{'_NO_SETUP_KLUDGE'}; my $part_pkg = $self->part_pkg; - #my $setup = $part_pkg->can('base_setup') ? $part_pkg->base_setup - # : $part_pkg->option('setup_fee'); - my $setup = $part_pkg->option('setup_fee'); - #XXX discounts - $setup *= $self->quantity if $self->quantity; - sprintf('%.2f', $setup); + my $quantity = $self->quantity || 1; + my ($unitsetup, $unitrecur); + # calculate base fees + if ( $self->waive_setup eq 'Y' || $self->{'_NO_SETUP_KLUDGE'} ) { + $unitsetup = '0.00'; + } else { + $unitsetup = $part_pkg->base_setup; + } + if ( $self->{'_NO_RECUR_KLUDGE'} ) { + $unitrecur = '0.00'; + } else { + $unitrecur = $part_pkg->base_recur; + } + + #XXX add-on packages + + $self->set('unitsetup', $unitsetup); + $self->set('unitrecur', $unitrecur); + my $error = $self->replace; + return $error if $error; + + # semi-duplicates calc_discount + my $setup_discount = 0; + my $recur_discount = 0; + + my %setup_discounts; # quotationpkgdiscountnum => amount + my %recur_discounts; # quotationpkgdiscountnum => amount + + # XXX the order of applying discounts is ill-defined, which matters + # if there are percentage and amount discounts on the same package. + foreach my $pkg_discount ($self->quotation_pkg_discount) { + + my $discount = $pkg_discount->discount; + my $this_setup_discount = 0; + my $this_recur_discount = 0; + + if ( $discount->percent > 0 ) { + + if ( $discount->setup ) { + $this_setup_discount = ($discount->percent * $unitsetup / 100); + } + $this_recur_discount = ($discount->percent * $unitrecur / 100); + + } elsif ( $discount->amount > 0 ) { + + my $discount_left = $discount->amount; + if ( $discount->setup ) { + if ( $discount_left > $unitsetup - $setup_discount ) { + # then discount the setup to zero + $discount_left -= $unitsetup - $setup_discount; + $this_setup_discount = $unitsetup - $setup_discount; + } else { + # not enough discount to fully cover the setup + $this_setup_discount = $discount_left; + $discount_left = 0; + } + } + # same logic for recur + if ( $discount_left > $unitrecur - $recur_discount ) { + $this_recur_discount = $unitrecur - $recur_discount; + } else { + $this_recur_discount = $discount_left; + } + + } + + # increment the total discountage + $setup_discount += $this_setup_discount; + $recur_discount += $this_recur_discount; + # and update the pkg_discount object + $pkg_discount->set('setup_amount', sprintf('%.2f', $setup_discount)); + $pkg_discount->set('recur_amount', sprintf('%.2f', $recur_discount)); + my $error = $pkg_discount->replace; + return $error if $error; + } + ''; } -sub recur { +=item insert_discount + +Associates this package with a discount (see L, +possibly inserting a new discount on the fly (see L). Properties +of the discount will be taken from this object. + +=cut + +sub insert_discount { + #my ($self, %options) = @_; my $self = shift; - return '0.00' if $self->{'_NO_RECUR_KLUDGE'}; - my $part_pkg = $self->part_pkg; - my $recur = $part_pkg->can('base_recur') ? $part_pkg->base_recur - : $part_pkg->option('recur_fee'); - #XXX discounts - $recur *= $self->quantity if $self->quantity; - sprintf('%.2f', $recur); + + my $cust_pkg_discount = FS::quotation_pkg_discount->new( { + 'quotationpkgnum' => $self->quotationpkgnum, + 'discountnum' => $self->discountnum, + #for the create a new discount case + '_type' => $self->discountnum__type, + 'amount' => $self->discountnum_amount, + 'percent' => $self->discountnum_percent, + 'months' => $self->discountnum_months, + 'setup' => $self->discountnum_setup, + } ); + + $cust_pkg_discount->insert; } -sub unitsetup { +sub _item_discount { my $self = shift; - return '0.00' if $self->waive_setup eq 'Y' || $self->{'_NO_SETUP_KLUDGE'}; - my $part_pkg = $self->part_pkg; - my $setup = $part_pkg->option('setup_fee'); + my @pkg_discounts = $self->pkg_discount; + return if @pkg_discounts == 0; + + my @ext; + my $d = { + _is_discount => 1, + description => $self->mt('Discount'), + setup_amount => 0, + recur_amount => 0, + amount => 0, + ext_description => \@ext, + # maybe should show quantity/unit discount? + }; + foreach my $pkg_discount (@pkg_discounts) { + push @ext, $pkg_discount->description; + $d->{setup_amount} -= $pkg_discount->setup_amount; + $d->{recur_amount} -= $pkg_discount->recur_amount; + } + $d->{setup_amount} *= $self->quantity || 1; + $d->{recur_amount} *= $self->quantity || 1; + $d->{amount} = $d->{setup_amount} + $d->{recur_amount}; + + return $d; +} - #XXX discounts - sprintf('%.2f', $setup); +sub setup { + my $self = shift; + ($self->unitsetup - sum(map { $_->setup_amount } $self->pkg_discount)) + * ($self->quantity || 1); } -sub unitrecur { +sub recur { my $self = shift; - return '0.00' if $self->{'_NO_RECUR_KLUDGE'}; - my $part_pkg = $self->part_pkg; - my $recur = $part_pkg->can('base_recur') ? $part_pkg->base_recur - : $part_pkg->option('recur_fee'); - #XXX discounts - sprintf('%.2f', $recur); + ($self->unitrecur - sum(map { $_->recur_amount } $self->pkg_discount)) + * ($self->quantity || 1); } =item cust_bill_pkg_display [ type => TYPE ] @@ -237,6 +418,8 @@ sub cust_main { =head1 BUGS +Doesn't support taxes, fees, or add-on packages. + =head1 SEE ALSO L, schema.html from the base documentation. diff --git a/FS/FS/quotation_pkg_discount.pm b/FS/FS/quotation_pkg_discount.pm index 34e13a610..15b1bfeb8 100644 --- a/FS/FS/quotation_pkg_discount.pm +++ b/FS/FS/quotation_pkg_discount.pm @@ -3,6 +3,7 @@ package FS::quotation_pkg_discount; use strict; use base qw( FS::Record ); use FS::Record qw( qsearch qsearchs ); +use FS::Maketext 'mt'; =head1 NAME @@ -37,12 +38,21 @@ primary key =item quotationpkgnum -quotationpkgnum +quotationpkgnum of the L record that this discount is +for. =item discountnum -discountnum +discountnum (L) +=item setup_amount + +Amount that will be discounted from setup fees, per package quantity. + +=item recur_amount + +Amount that will be discounted from recurring fees in the first billing +cycle, per package quantity. =back @@ -108,6 +118,8 @@ sub check { $self->ut_numbern('quotationpkgdiscountnum') || $self->ut_foreign_key('quotationpkgnum', 'quotation_pkg', 'quotationpkgnum' ) || $self->ut_foreign_key('discountnum', 'discount', 'discountnum' ) + || $self->ut_moneyn('setup_amount') + || $self->ut_moneyn('recur_amount') ; return $error if $error; @@ -116,6 +128,39 @@ sub check { =back +=item amount + +Returns the total amount of this discount (setup + recur), for compatibility +with L. + +=cut + +sub amount { + my $self = shift; + return $self->get('setup_amount') + $self->get('recur_amount'); +} + +=item description + +Returns a string describing the discount (for use on the quotation). + +=cut + +sub description { + my $self = shift; + my $discount = $self->discount; + my $desc = $discount->description_short; + # XXX localize to prospect language, once prospects get languages + $desc .= mt(' each') if $self->quotation_pkg->quantity > 1; + + if ($discount->months) { + # unlike cust_bill_pkg_discount, there are no "months remaining"; it + # hasn't started yet. + $desc .= mt(' (for [quant,_1,month])', $discount->months); + } + return $desc; +} + =head1 BUGS =head1 SEE ALSO diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index 38d5c4486..67cdb87e8 100644 --- a/httemplate/edit/process/quick-cust_pkg.cgi +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -144,7 +144,7 @@ if ( $quotationnum ) { $quotation_pkg->prospectnum($prospect_main->prospectnum) if $prospect_main; #XXX handle new location - $error = $quotation_pkg->insert; + $error = $quotation_pkg->insert || $quotation_pkg->estimate; } else { -- 2.11.0