2 use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin FS::Record
14 FS::quotation - Object methods for quotation records
20 $record = new FS::quotation \%hash;
21 $record = new FS::quotation { 'column' => 'value' };
23 $error = $record->insert;
25 $error = $new_record->replace($old_record);
27 $error = $record->delete;
29 $error = $record->check;
33 An FS::quotation object represents a quotation. FS::quotation inherits from
34 FS::Record. The following fields are currently supported:
71 Creates a new quotation. To add the quotation to the database, see L<"insert">.
73 Note that this stores the hash reference, not a distinct copy of the hash it
74 points to. You can ask the object for a copy with the I<hash> method.
78 sub table { 'quotation'; }
79 sub notice_name { 'Quotation'; }
80 sub template_conf { 'quotation_'; }
84 Adds this record to the database. If there is an error, returns the error,
85 otherwise returns false.
89 Delete this record from the database.
91 =item replace OLD_RECORD
93 Replaces the OLD_RECORD with this one in the database. If there is an error,
94 returns the error, otherwise returns false.
98 Checks all fields to make sure this is a valid quotation. If there is
99 an error, returns the error, otherwise returns false. Called by the insert
108 $self->ut_numbern('quotationnum')
109 || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum' )
110 || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum' )
111 || $self->ut_numbern('_date')
112 || $self->ut_enum('disabled', [ '', 'Y' ])
113 || $self->ut_numbern('usernum')
115 return $error if $error;
117 $self->_date(time) unless $self->_date;
119 $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
121 return 'prospectnum or custnum must be specified'
122 if ! $self->prospectnum
136 sub cust_bill_pkg { #actually quotation_pkg objects
137 shift->quotation_pkg(@_);
146 $self->_total('setup');
149 =item total_recur [ FREQ ]
155 #=item total_recur [ FREQ ]
156 #my $freq = @_ ? shift : '';
157 $self->_total('recur');
161 my( $self, $method ) = @_;
164 $total += $_->$method() for $self->cust_bill_pkg;
165 sprintf('%.2f', $total);
169 #prevent things from falsely showing up as taxes, at least until we support
170 # quoting tax amounts..
175 shift->cust_bill_pkg;
179 my( $self, $total_items ) = @_;
181 if ( $self->total_setup > 0 ) {
182 push @$total_items, {
183 'total_item' => $self->mt( $self->total_recur > 0 ? 'Total Setup' : 'Total' ),
184 'total_amount' => $self->total_setup,
188 #could/should add up the different recurring frequencies on lines of their own
189 # but this will cover the 95% cases for now
190 if ( $self->total_recur > 0 ) {
191 push @$total_items, {
192 'total_item' => $self->mt('Total Recurring'),
193 'total_amount' => $self->total_recur,
199 =item enable_previous
203 sub enable_previous { 0 }
205 =item convert_cust_main
207 If this quotation already belongs to a customer, then returns that customer, as
208 an FS::cust_main object.
210 Otherwise, creates a new customer (FS::cust_main object and record, and
211 associated) based on this quotation's prospect, then orders this quotation's
212 packages as real packages for the customer.
214 If there is an error, returns an error message, otherwise, returns the
215 newly-created FS::cust_main object.
219 sub convert_cust_main {
222 my $cust_main = $self->cust_main;
223 return $cust_main if $cust_main; #already converted, don't again
225 my $oldAutoCommit = $FS::UID::AutoCommit;
226 local $FS::UID::AutoCommit = 0;
229 $cust_main = $self->prospect_main->convert_cust_main;
230 unless ( ref($cust_main) ) { # eq 'FS::cust_main' ) {
231 $dbh->rollback if $oldAutoCommit;
235 $self->prospectnum('');
236 $self->custnum( $cust_main->custnum );
237 my $error = $self->replace || $self->order;
239 $dbh->rollback if $oldAutoCommit;
243 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
251 This method is for use with quotations which are already associated with a customer.
253 Orders this quotation's packages as real packages for the customer.
255 If there is an error, returns an error message, otherwise returns false.
262 tie my %cust_pkg, 'Tie::RefHash',
263 map { FS::cust_pkg->new({ pkgpart => $_->pkgpart,
264 quantity => $_->quantity,
268 $self->quotation_pkg ;
270 $self->cust_main->order_pkgs( \%cust_pkg );
281 =item search_sql_where HASHREF
283 Class method which returns an SQL WHERE fragment to search for parameters
284 specified in HASHREF. Valid parameters are
290 List reference of start date, end date, as UNIX timestamps.
300 List reference of charged limits (exclusive).
304 List reference of charged limits (exclusive).
308 flag, return open invoices only
312 flag, return net invoices only
320 Note: validates all passed-in data; i.e. safe to use with unchecked CGI params.
324 sub search_sql_where {
325 my($class, $param) = @_;
327 # warn "$me search_sql_where called with params: \n".
328 # join("\n", map { " $_: ". $param->{$_} } keys %$param ). "\n";
334 if ( $param->{'agentnum'} =~ /^(\d+)$/ ) {
335 push @search, "( prospect_main.agentnum = $1 OR cust_main.agentnum = $1 )";
339 # if ( $param->{'refnum'} =~ /^(\d+)$/ ) {
340 # push @search, "cust_main.refnum = $1";
344 if ( $param->{'prospectnum'} =~ /^(\d+)$/ ) {
345 push @search, "quotation.prospectnum = $1";
349 if ( $param->{'custnum'} =~ /^(\d+)$/ ) {
350 push @search, "cust_bill.custnum = $1";
354 if ( $param->{_date} ) {
355 my($beginning, $ending) = @{$param->{_date}};
357 push @search, "quotation._date >= $beginning",
358 "quotation._date < $ending";
362 if ( $param->{'quotationnum_min'} =~ /^(\d+)$/ ) {
363 push @search, "quotation.quotationnum >= $1";
365 if ( $param->{'quotationnum_max'} =~ /^(\d+)$/ ) {
366 push @search, "quotation.quotationnum <= $1";
370 # if ( $param->{charged} ) {
371 # my @charged = ref($param->{charged})
372 # ? @{ $param->{charged} }
373 # : ($param->{charged});
375 # push @search, map { s/^charged/cust_bill.charged/; $_; }
379 my $owed_sql = FS::cust_bill->owed_sql;
382 push @search, "quotation._date < ". (time-86400*$param->{'days'})
385 #agent virtualization
386 my $curuser = $FS::CurrentUser::CurrentUser;
387 #false laziness w/search/quotation.html
388 push @search,' ( '. $curuser->agentnums_sql( table=>'prospect_main' ).
389 ' OR '. $curuser->agentnums_sql( table=>'cust_main' ).
392 join(' AND ', @search );
402 L<FS::Record>, schema.html from the base documentation.