1 package FS::ClientAPI::MyAccount::quotation;
4 use FS::Record qw(qsearch qsearchs);
10 sub _custoragent_session_custnum {
11 FS::ClientAPI::MyAccount::_custoragent_session_custnum(@_);
14 # _quotation(session, quotationnum)
15 # returns that quotation, or '' if it doesn't exist and belong to this
20 my $quotationnum = shift;
23 if ( $quotationnum =~ /^(\d+)$/ ) {
24 $quotation = qsearchs( 'quotation', {
25 'custnum' => $session->{'custnum'},
26 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
30 warn "found selfservice quotation #". $quotation->quotationnum."\n"
31 if $quotation and $DEBUG;
38 =item list_quotations { session }
40 Returns a hashref listing this customer's active self-service quotations.
43 - 'quotations', an arrayref containing an element for each quotation.
44 - quotationnum, the primary key
45 - _date, the date it was started
46 - num_pkgs, the number of packages
47 - total_setup, the sum of setup fees
48 - total_recur, the sum of recurring charges
55 my($context, $session, $custnum) = _custoragent_session_custnum($p);
56 return { 'error' => $session } if $context eq 'error';
58 my @quotations = qsearch('quotation', {
59 'custnum' => $session->{'custnum'},
60 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
64 foreach my $quotation (@quotations) {
65 warn "found selfservice quotation #". $quotation->quotationnum."\n"
66 if $quotation and $DEBUG;
67 push @q, { 'quotationnum' => $quotation->quotationnum,
68 '_date' => $quotation->_date,
69 'num_pkgs' => scalar($quotation->quotation_pkg),
70 'total_setup' => $quotation->total_setup,
71 'total_recur' => $quotation->total_recur,
74 return { 'quotations' => \@q, 'error' => '' };
77 =item quotation_new { session }
79 Creates a quotation and returns its quotationnum.
86 my($context, $session, $custnum) = _custoragent_session_custnum($p);
87 return { 'error' => $session } if $context eq 'error';
89 my $quotation = FS::quotation->new({
90 'custnum' => $session->{'custnum'},
91 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
94 my $error = $quotation->insert;
96 warn "failed to create selfservice quotation for custnum #" .
97 $session->{custnum} . "\n";
98 return { 'error' => $error };
100 warn "started new selfservice quotation #". $quotation->quotationnum."\n"
102 return { 'error' => $error, 'quotationnum' => $quotation->quotationnum };
106 =item quotation_delete { session, quotationnum }
108 Disables (doesn't actually delete) the specified quotationnum.
112 sub quotation_delete {
115 my($context, $session, $custnum) = _custoragent_session_custnum($p);
116 return { 'error' => $session } if $context eq 'error';
118 my $quotation = _quotation($session, $p->{quotationnum})
119 or return { 'error' => "Quotation not found" };
120 warn "quotation_delete #".$quotation->quotationnum
123 $quotation->set('disabled' => 'Y');
124 my $error = $quotation->replace;
125 return { 'error' => $error };
128 =item quotation_info { session, quotationnum }
130 Returns a hashref describing the specified quotation, containing:
132 - "sections", an arrayref containing one section for each billing frequency.
136 - "detail_items", an arrayref of detail items, each with:
137 - "pkgnum", the reference number (actually the quotationpkgnum field)
138 - "description", the package name (or tax name)
141 - "num_pkgs", the number of packages in the quotation
142 - "total_setup", the sum of setup/one-time charges and their taxes
143 - "total_recur", the sum of all recurring charges and their taxes
150 my($context, $session, $custnum) = _custoragent_session_custnum($p);
151 return { 'error' => $session } if $context eq 'error';
153 my $quotation = _quotation($session, $p->{quotationnum})
154 or return { 'error' => "Quotation not found" };
155 warn "quotation_info #".$quotation->quotationnum
159 my $null_escape = sub { @_ };
160 my ($sections) = $quotation->_items_sections(escape => $null_escape);
161 foreach my $section (@$sections) {
162 $section->{'detail_items'} =
163 [ $quotation->_items_pkg('section' => $section, escape_function => $null_escape) ];
165 return { 'error' => '',
166 'sections' => $sections,
167 'num_pkgs' => scalar($quotation->quotation_pkg),
168 'total_setup' => $quotation->total_setup,
169 'total_recur' => $quotation->total_recur,
173 =item quotation_print { session, 'format' }
175 Renders the quotation. 'format' can be either 'html' or 'pdf'; the resulting
176 hashref will contain 'document' => the HTML or PDF contents.
180 sub quotation_print {
183 my($context, $session, $custnum) = _custoragent_session_custnum($p);
184 return { 'error' => $session } if $context eq 'error';
186 my $quotation = _quotation($session, $p->{quotationnum})
187 or return { 'error' => "Quotation not found" };
188 warn "quotation_print #".$quotation->quotationnum
191 my $format = $p->{'format'}
192 or return { 'error' => "No rendering format specified" };
195 if ($format eq 'html') {
196 $document = $quotation->print_html;
197 } elsif ($format eq 'pdf') {
198 $document = $quotation->print_pdf;
200 warn "$format, ".length($document)." bytes\n"
202 return { 'error' => '', 'document' => $document };
205 =item quotation_add_pkg { session, 'pkgpart', 'quantity', [ location opts ] }
207 Adds a package to the user's current quotation. Session info and 'pkgpart' are
208 required. 'quantity' defaults to 1.
210 Location can be specified as 'locationnum' to use an existing location, or
211 'address1', 'address2', 'city', 'state', 'zip', 'country' to create a new one,
212 or it will default to the customer's service location.
216 sub quotation_add_pkg {
219 my($context, $session, $custnum) = _custoragent_session_custnum($p);
220 return { 'error' => $session } if $context eq 'error';
222 my $quotation = _quotation($session, $p->{quotationnum})
223 or return { 'error' => "Quotation not found" };
224 my $cust_main = $quotation->cust_main;
226 my $pkgpart = $p->{'pkgpart'};
227 my $allowed_pkgpart = $cust_main->agent->pkgpart_hashref;
229 my $part_pkg = FS::part_pkg->by_key($pkgpart);
231 if (!$part_pkg or !$allowed_pkgpart->{$pkgpart}) {
232 warn "disallowed quotation_pkg pkgpart $pkgpart\n"
234 return { 'error' => "unknown package $pkgpart" };
237 warn "creating quotation_pkg with pkgpart $pkgpart\n"
239 my $quotation_pkg = FS::quotation_pkg->new({
240 'quotationnum' => $quotation->quotationnum,
241 'pkgpart' => $p->{'pkgpart'},
242 'quantity' => $p->{'quantity'} || 1,
244 if ( $p->{locationnum} > 0 ) {
245 $quotation_pkg->set('locationnum', $p->{locationnum});
246 } elsif ( $p->{address1} ) {
247 my $location = FS::cust_location->find_or_insert(
248 'custnum' => $cust_main->custnum,
249 map { $_ => $p->{$_} }
250 qw( address1 address2 city county state zip country )
252 $quotation_pkg->set('locationnum', $location->locationnum);
255 my $error = $quotation_pkg->insert
256 || $quotation->estimate
260 'quotationnum' => $quotation->quotationnum };
263 =item quotation_remove_pkg { session, 'pkgnum' }
265 Removes the package from the user's current quotation. 'pkgnum' is required.
269 sub quotation_remove_pkg {
272 my($context, $session, $custnum) = _custoragent_session_custnum($p);
273 return { 'error' => $session } if $context eq 'error';
275 my $quotation = _quotation($session, $p->{quotationnum})
276 or return { 'error' => "Quotation not found" };
277 my $quotationpkgnum = $p->{pkgnum};
278 my $quotation_pkg = FS::quotation_pkg->by_key($quotationpkgnum);
280 or $quotation_pkg->quotationnum != $quotation->quotationnum) {
281 return { 'error' => "unknown quotation item $quotationpkgnum" };
283 warn "removing quotation_pkg with pkgpart ".$quotation_pkg->pkgpart."\n"
286 my $error = $quotation_pkg->delete
287 || $quotation->estimate;
290 'quotationnum' => $quotation->quotationnum };
293 =item quotation_order
295 Convert the current quotation to a package order.
299 sub quotation_order {
302 my($context, $session, $custnum) = _custoragent_session_custnum($p);
303 return { 'error' => $session } if $context eq 'error';
305 my $quotation = _quotation($session, $p->{quotationnum})
306 or return { 'error' => "Quotation not found" };
308 my $error = $quotation->order;
309 $quotation->set('disabled' => 'Y');
310 $error ||= $quotation->replace;
312 return { 'error' => $error };