selfservice quotations, #33852
[freeside.git] / FS / FS / ClientAPI / MyAccount / quotation.pm
diff --git a/FS/FS/ClientAPI/MyAccount/quotation.pm b/FS/FS/ClientAPI/MyAccount/quotation.pm
new file mode 100644 (file)
index 0000000..ce2debd
--- /dev/null
@@ -0,0 +1,218 @@
+package FS::ClientAPI::MyAccount::quotation;
+
+use strict;
+use FS::Record qw(qsearch qsearchs);
+use FS::quotation;
+use FS::quotation_pkg;
+
+our $DEBUG = 1;
+
+sub _custoragent_session_custnum {
+  FS::ClientAPI::MyAccount::_custoragent_session_custnum(@_);
+}
+
+sub _quotation {
+  # the currently active quotation
+  my $session = shift;
+  my $quotation;
+  if ( my $quotationnum = $session->{'quotationnum'} ) {
+    $quotation = FS::quotation->by_key($quotationnum);
+  } 
+  if ( !$quotation ) {
+    # find the last quotation created through selfservice
+    $quotation = qsearchs( 'quotation', {
+        'custnum'   => $session->{'custnum'},
+        'usernum'   => $FS::CurrentUser::CurrentUser->usernum,
+        'disabled'  => '',
+    }); 
+    warn "found selfservice quotation #". $quotation->quotationnum."\n"
+      if $quotation and $DEBUG;
+  } 
+  if ( !$quotation ) {
+    $quotation = FS::quotation->new({
+        'custnum'   => $session->{'custnum'},
+        'usernum'   => $FS::CurrentUser::CurrentUser->usernum,
+        '_date'     => time,
+    }); 
+    $quotation->insert; # what to do on error? call the police?
+    warn "started new selfservice quotation #". $quotation->quotationnum."\n"
+      if $quotation and $DEBUG;
+  } 
+  $session->{'quotationnum'} = $quotation->quotationnum;
+  return $quotation;
+}
+
+=item quotation_info { session }
+
+Returns a hashref describing the current quotation, containing:
+
+- "sections", an arrayref containing one section for each billing frequency.
+  Each one will have:
+  - "description"
+  - "subtotal"
+  - "detail_items", an arrayref of detail items, each with:
+    - "pkgnum", the reference number (actually the quotationpkgnum field)
+    - "description", the package name (or tax name)
+    - "quantity"
+    - "amount"
+
+=cut
+
+sub quotation_info {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+
+  my $quotation = _quotation($session);
+  return { 'error' => "No current quotation for this customer" } if !$quotation;
+  warn "quotation_info #".$quotation->quotationnum
+    if $DEBUG;
+
+  # code reuse ftw
+  my $null_escape = sub { @_ };
+  my ($sections) = $quotation->_items_sections(escape => $null_escape);
+  foreach my $section (@$sections) {
+    $section->{'detail_items'} =
+      [ $quotation->_items_pkg('section' => $section, escape_function => $null_escape) ]; 
+  }
+  return { 'error' => '', 'sections' => $sections }
+}
+
+=item quotation_print { session, 'format' }
+
+Renders the quotation. 'format' can be either 'html' or 'pdf'; the resulting
+hashref will contain 'document' => the HTML or PDF contents.
+
+=cut
+
+sub quotation_print {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+
+  my $quotation = _quotation($session);
+  return { 'error' => "No current quotation for this customer" } if !$quotation;
+  warn "quotation_print #".$quotation->quotationnum
+    if $DEBUG;
+
+  my $format = $p->{'format'}
+   or return { 'error' => "No rendering format specified" };
+
+  my $document;
+  if ($format eq 'html') {
+    $document = $quotation->print_html;
+  } elsif ($format eq 'pdf') {
+    $document = $quotation->print_pdf;
+  }
+  warn "$format, ".length($document)." bytes\n"
+    if $DEBUG;
+  return { 'error' => '', 'document' => $document };
+}
+
+=item quotation_add_pkg { session, 'pkgpart', 'quantity', [ location opts ] }
+
+Adds a package to the user's current quotation. Session info and 'pkgpart' are
+required. 'quantity' defaults to 1.
+
+Location can be specified as 'locationnum' to use an existing location, or
+'address1', 'address2', 'city', 'state', 'zip', 'country' to create a new one,
+or it will default to the customer's service location.
+
+=cut
+
+sub quotation_add_pkg {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+  
+  my $quotation = _quotation($session);
+  my $cust_main = $quotation->cust_main;
+
+  my $pkgpart = $p->{'pkgpart'};
+  my $allowed_pkgpart = $cust_main->agent->pkgpart_hashref;
+
+  my $part_pkg = FS::part_pkg->by_key($pkgpart);
+
+  if (!$part_pkg or !$allowed_pkgpart->{$pkgpart}) {
+    warn "disallowed quotation_pkg pkgpart $pkgpart\n"
+      if $DEBUG;
+    return { 'error' => "unknown package $pkgpart" };
+  }
+
+  warn "creating quotation_pkg with pkgpart $pkgpart\n"
+    if $DEBUG;
+  my $quotation_pkg = FS::quotation_pkg->new({
+    'quotationnum'  => $quotation->quotationnum,
+    'pkgpart'       => $p->{'pkgpart'},
+    'quantity'      => $p->{'quantity'} || 1,
+  });
+  if ( $p->{locationnum} > 0 ) {
+    $quotation_pkg->set('locationnum', $p->{locationnum});
+  } elsif ( $p->{address1} ) {
+    my $location = FS::cust_location->find_or_insert(
+      'custnum' => $cust_main->custnum,
+      map { $_ => $p->{$_} }
+        qw( address1 address2 city county state zip country )
+    );
+    $quotation_pkg->set('locationnum', $location->locationnum);
+  }
+
+  my $error = $quotation_pkg->insert
+           || $quotation->estimate;
+
+  { 'error'         => $error,
+    'quotationnum'  => $quotation->quotationnum };
+}
+=item quotation_remove_pkg { session, 'pkgnum' }
+
+Removes the package from the user's current quotation. 'pkgnum' is required.
+
+=cut
+
+sub quotation_remove_pkg {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+  
+  my $quotation = _quotation($session);
+  my $quotationpkgnum = $p->{pkgnum};
+  my $quotation_pkg = FS::quotation_pkg->by_key($quotationpkgnum);
+  if (!$quotation_pkg
+      or $quotation_pkg->quotationnum != $quotation->quotationnum) {
+    return { 'error' => "unknown quotation item $quotationpkgnum" };
+  }
+  warn "removing quotation_pkg with pkgpart ".$quotation_pkg->pkgpart."\n"
+    if $DEBUG;
+
+  my $error = $quotation_pkg->delete
+           || $quotation->estimate;
+
+  { 'error'         => $error,
+    'quotationnum'  => $quotation->quotationnum };
+}
+
+=item quotation_order
+
+Convert the current quotation to a package order.
+
+=cut
+
+sub quotation_order {
+  my $p = shift;
+
+  my($context, $session, $custnum) = _custoragent_session_custnum($p);
+  return { 'error' => $session } if $context eq 'error';
+  
+  my $quotation = _quotation($session);
+
+  my $error = $quotation->order;
+
+  return { 'error' => $error };
+}
+
+1;