+#it looks redundant with a v4.x+ auto-generated method, but need to override
+# FS::TemplateItem_Mixin's version
+sub part_pkg {
+ my $self = shift;
+ qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
+sub desc {
+ my $self = shift;
+ $self->part_pkg->pkg;
+}
+
+=cut
+
+=item insert_discount
+
+Associates this package with a discount (see L<FS::cust_pkg_discount>,
+possibly inserting a new discount on the fly (see L<FS::discount>). Properties
+of the discount will be taken from this object.
+
+=cut
+
+sub insert_discount {
+ #my ($self, %options) = @_;
+ my $self = shift;
+
+ foreach my $x (qw(setup recur)) {
+ if ( my $discountnum = $self->get("${x}_discountnum") ) {
+ my $cust_pkg_discount = FS::quotation_pkg_discount->new( {
+ 'quotationpkgnum' => $self->quotationpkgnum,
+ 'discountnum' => $discountnum,
+ 'setuprecur' => $x,
+ #for the create a new discount case
+ 'amount' => $self->get("${x}_discountnum_amount"),
+ 'percent' => $self->get("${x}_discountnum_percent"),
+ 'months' => $self->get("${x}_discountnum_months"),
+ } );
+ if ( $x eq 'setup' ) {
+ $cust_pkg_discount->setup('Y');
+ $cust_pkg_discount->months('');
+ }
+ my $error = $cust_pkg_discount->insert;
+ return $error if $error;
+ }
+ }
+}
+
+sub _item_discount {
+ my $self = shift;
+ my %options = @_;
+ my $setuprecur = $options{'setuprecur'};
+ # a little different from cust_bill_pkg::_item_discount, in that this one
+ # is asked specifically whether to show setup or recur discounts (because
+ # on the quotation they're separate sections entirely)
+
+ my @pkg_discounts = grep { $_->setuprecur eq $setuprecur }
+ $self->pkg_discount;
+ return if @pkg_discounts == 0;
+
+ 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;
+ my $amount = $pkg_discount->get('amount');
+ $d->{amount} -= $amount;
+ }
+ $d->{amount} = sprintf('%.2f', $d->{amount} * $self->quantity);
+
+ return $d;
+}
+
+sub setup {
+ my $self = shift;
+ return '0.00' if $self->waive_setup eq 'Y';;
+ my $discount_amount = sum(0, map { $_->amount }
+ grep { $_->setuprecur eq 'setup' }
+ $self->pkg_discount
+ );
+ ($self->unitsetup - $discount_amount) * ($self->quantity || 1);
+
+}
+
+sub setup_show_zero {
+ my $self = shift;
+ return $self->part_pkg->setup_show_zero;
+}
+
+sub setup_tax {
+ my $self = shift;
+ sum(0, map { $_->setup_amount } $self->quotation_pkg_tax);
+}
+
+sub recur {
+ my $self = shift;
+ my $discount_amount = sum(0, map { $_->amount }
+ grep { $_->setuprecur eq 'recur' }
+ $self->pkg_discount
+ );
+ ($self->unitrecur - $discount_amount) * ($self->quantity || 1);
+
+}
+
+sub recur_show_zero {
+ my $self = shift;
+ return $self->part_pkg->recur_show_zero;
+}
+
+sub recur_tax {
+ my $self = shift;
+ sum(0, map { $_->recur_amount } $self->quotation_pkg_tax);
+}
+
+=item part_pkg_currency_option OPTIONNAME
+
+Returns a two item list consisting of the currency of this quotation's customer
+or prospect, if any, and a value for the provided option. If the customer or
+prospect has a currency, the value is the option value the given name and the
+currency (see L<FS::part_pkg_currency>). Otherwise, if the customer or
+prospect has no currency, is the regular option value for the given name (see
+L<FS::part_pkg_option>).
+
+=cut
+
+#false laziness w/cust_pkg->part_pkg_currency_option
+sub part_pkg_currency_option {
+ my( $self, $optionname ) = @_;
+ my $part_pkg = $self->part_pkg;
+ my $prospect_or_customer = $self->cust_main || $self->prospect_main;
+ if ( my $currency = $prospect_or_customer->currency ) {
+ ($currency, $part_pkg->part_pkg_currency_option($currency, $optionname) );
+ } else {
+ ('', $part_pkg->option($optionname) );
+ }
+}
+
+=item delete_details
+
+Deletes all quotation_pkgs_details associated with this pkg (see L<FS::quotation_pkg_detail>).
+
+=cut
+
+sub delete_details {
+ my $self = shift;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $detail ( qsearch('quotation_pkg_detail',{ 'quotationpkgnum' => $self->quotationpkgnum }) ) {
+ my $error = $detail->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error removing old detail: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item set_details PARAM
+
+Sets new quotation details for this package (see L<FS::quotation_pkg_detail>),
+removing existing details.
+
+Recognizes the following parameters:
+
+details - arrayref of strings, one for each new detail
+
+copy_on_order - if true, sets copy_on_order flag on new details
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub set_details {
+ my $self = shift;
+ my %opt = @_;
+
+ $opt{'details'} ||= [];
+ my @details = @{$opt{'details'}};
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->delete_details;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $detail ( @details ) {
+ my $quotation_pkg_detail = new FS::quotation_pkg_detail {
+ 'quotationpkgnum' => $self->quotationpkgnum,
+ 'detail' => $detail,
+ 'copy_on_order' => $opt{'copy_on_order'} ? 'Y' : '',
+ };
+ $error = $quotation_pkg_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding new detail: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+sub details_header {
+ return ();
+}
+
+=item cust_bill_pkg_display [ type => TYPE ]
+
+=cut
+
+sub cust_bill_pkg_display {
+ my ( $self, %opt ) = @_;
+
+ my $type = $opt{type} if exists $opt{type};
+ return () if $type eq 'U'; #quotations don't have usage
+
+ if ( $self->get('display') ) {
+ return ( grep { defined($type) ? ($type eq $_->type) : 1 }
+ @{ $self->get('display') }
+ );
+ } else {
+
+ #??
+ my $setup = $self->new($self->hashref);
+ $setup->{'_NO_RECUR_KLUDGE'} = 1;
+ $setup->{'type'} = 'S';
+ my $recur = $self->new($self->hashref);
+ $recur->{'_NO_SETUP_KLUDGE'} = 1;
+ $recur->{'type'} = 'R';
+
+ if ( $type eq 'S' ) {
+ return ($setup);
+ } elsif ( $type eq 'R' ) {
+ return ($recur);
+ } else {
+ #return ($setup, $recur);
+ return ($self);
+ }
+
+ }
+
+}
+
+=item cust_main
+
+Returns the customer (L<FS::cust_main> object).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ my $quotation = $self->quotation or return '';
+ $quotation->cust_main;
+}
+
+=item prospect_main
+
+Returns the prospect (L<FS::prospect_main> object).
+
+=cut
+
+sub prospect_main {
+ my $self = shift;
+ my $quotation = $self->quotation or return '';
+ $quotation->prospect_main;
+}
+
+sub tax_locationnum {
+ my $self = shift;
+ $self->locationnum;
+}
+
+sub _upgrade_data {
+ my $class = shift;
+ my @quotation_pkg_without_location =
+ qsearch( 'quotation_pkg', { locationnum => '' } );
+ if (@quotation_pkg_without_location) {
+ warn "setting default location on quotation_pkg records\n";
+ foreach my $quotation_pkg (@quotation_pkg_without_location) {
+ # check() will fix this
+ my $error = $quotation_pkg->replace;
+ if ($error) {
+ die "quotation #".$quotation_pkg->quotationnum.": $error\n";
+ }
+ }
+ }
+ '';
+}
+