2 use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin FS::Record );
7 use FS::Record qw( qsearch qsearchs );
11 use FS::prospect_main;
12 use FS::quotation_pkg;
16 FS::quotation - Object methods for quotation records
22 $record = new FS::quotation \%hash;
23 $record = new FS::quotation { 'column' => 'value' };
25 $error = $record->insert;
27 $error = $new_record->replace($old_record);
29 $error = $record->delete;
31 $error = $record->check;
35 An FS::quotation object represents a quotation. FS::quotation inherits from
36 FS::Record. The following fields are currently supported:
73 Creates a new quotation. To add the quotation to the database, see L<"insert">.
75 Note that this stores the hash reference, not a distinct copy of the hash it
76 points to. You can ask the object for a copy with the I<hash> method.
80 sub table { 'quotation'; }
81 sub notice_name { 'Quotation'; }
82 sub template_conf { 'quotation_'; }
86 Adds this record to the database. If there is an error, returns the error,
87 otherwise returns false.
91 Delete this record from the database.
93 =item replace OLD_RECORD
95 Replaces the OLD_RECORD with this one in the database. If there is an error,
96 returns the error, otherwise returns false.
100 Checks all fields to make sure this is a valid quotation. If there is
101 an error, returns the error, otherwise returns false. Called by the insert
110 $self->ut_numbern('quotationnum')
111 || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum' )
112 || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum' )
113 || $self->ut_numbern('_date')
114 || $self->ut_enum('disabled', [ '', 'Y' ])
115 || $self->ut_numbern('usernum')
117 return $error if $error;
119 $self->_date(time) unless $self->_date;
121 $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
132 qsearchs('prospect_main', { 'prospectnum' => $self->prospectnum } );
141 qsearchs('cust_main', { 'custnum' => $self->custnum } );
148 sub cust_bill_pkg { #actually quotation_pkg objects
150 qsearch('quotation_pkg', { quotationnum=>$self->quotationnum });
159 $self->_total('setup');
162 =item total_recur [ FREQ ]
168 #=item total_recur [ FREQ ]
169 #my $freq = @_ ? shift : '';
170 $self->_total('recur');
174 my( $self, $method ) = @_;
177 $total += $_->$method() for $self->cust_bill_pkg;
178 sprintf('%.2f', $total);
182 #prevent things from falsely showing up as taxes, at least until we support
183 # quoting tax amounts..
188 shift->cust_bill_pkg;
192 my( $self, $total_items ) = @_;
194 if ( $self->total_setup > 0 ) {
195 push @$total_items, {
196 'total_item' => $self->mt( $self->total_recur > 0 ? 'Total Setup' : 'Total' ),
197 'total_amount' => $self->total_setup,
201 #could/should add up the different recurring frequencies on lines of their own
202 # but this will cover the 95% cases for now
203 if ( $self->total_recur > 0 ) {
204 push @$total_items, {
205 'total_item' => $self->mt('Total Recurring'),
206 'total_amount' => $self->total_recur,
212 =item enable_previous
216 sub enable_previous { 0 }
218 =item convert_cust_main
220 If this quotation already belongs to a customer, then returns that customer, as
221 an FS::cust_main object.
223 Otherwise, creates a new customer (FS::cust_main object and record, and
224 associated) based on this quotation's prospect, then orders this quotation's
225 packages as real packages for the customer.
227 If there is an error, returns an error message, otherwise, returns the
228 newly-created FS::cust_main object.
232 sub convert_cust_main {
235 my $cust_main = $self->cust_main;
236 return $cust_main if $cust_main; #already converted, don't again
238 my $oldAutoCommit = $FS::UID::AutoCommit;
239 local $FS::UID::AutoCommit = 0;
242 $cust_main = $self->prospect_main->convert_cust_main;
243 unless ( ref($cust_main) ) { # eq 'FS::cust_main' ) {
244 $dbh->rollback if $oldAutoCommit;
248 $self->prospectnum('');
249 $self->custnum( $cust_main->custnum );
250 my $error = $self->replace || $self->order;
252 $dbh->rollback if $oldAutoCommit;
256 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
264 This method is for use with quotations which are already associated with a customer.
266 Orders this quotation's packages as real packages for the customer.
268 If there is an error, returns an error message, otherwise returns false.
275 tie my %cust_pkg, 'Tie::RefHash',
276 map { FS::cust_pkg->new({ pkgpart => $_->pkgpart,
277 quantity => $_->quantity,
281 $self->quotation_pkg ;
283 $self->cust_main->order_pkgs( \%cust_pkg );
294 =item search_sql_where HASHREF
296 Class method which returns an SQL WHERE fragment to search for parameters
297 specified in HASHREF. Valid parameters are
303 List reference of start date, end date, as UNIX timestamps.
313 List reference of charged limits (exclusive).
317 List reference of charged limits (exclusive).
321 flag, return open invoices only
325 flag, return net invoices only
333 Note: validates all passed-in data; i.e. safe to use with unchecked CGI params.
337 sub search_sql_where {
338 my($class, $param) = @_;
340 # warn "$me search_sql_where called with params: \n".
341 # join("\n", map { " $_: ". $param->{$_} } keys %$param ). "\n";
347 if ( $param->{'agentnum'} =~ /^(\d+)$/ ) {
348 push @search, "( prospect_main.agentnum = $1 OR cust_main.agentnum = $1 )";
352 # if ( $param->{'refnum'} =~ /^(\d+)$/ ) {
353 # push @search, "cust_main.refnum = $1";
357 if ( $param->{'prospectnum'} =~ /^(\d+)$/ ) {
358 push @search, "quotation.prospectnum = $1";
362 if ( $param->{'custnum'} =~ /^(\d+)$/ ) {
363 push @search, "cust_bill.custnum = $1";
367 if ( $param->{_date} ) {
368 my($beginning, $ending) = @{$param->{_date}};
370 push @search, "quotation._date >= $beginning",
371 "quotation._date < $ending";
375 if ( $param->{'quotationnum_min'} =~ /^(\d+)$/ ) {
376 push @search, "quotation.quotationnum >= $1";
378 if ( $param->{'quotationnum_max'} =~ /^(\d+)$/ ) {
379 push @search, "quotation.quotationnum <= $1";
383 # if ( $param->{charged} ) {
384 # my @charged = ref($param->{charged})
385 # ? @{ $param->{charged} }
386 # : ($param->{charged});
388 # push @search, map { s/^charged/cust_bill.charged/; $_; }
392 my $owed_sql = FS::cust_bill->owed_sql;
395 push @search, "quotation._date < ". (time-86400*$param->{'days'})
398 #agent virtualization
399 my $curuser = $FS::CurrentUser::CurrentUser;
400 #false laziness w/search/quotation.html
401 push @search,' ( '. $curuser->agentnums_sql( table=>'prospect_main' ).
402 ' OR '. $curuser->agentnums_sql( table=>'cust_main' ).
405 join(' AND ', @search );
415 L<FS::Record>, schema.html from the base documentation.