Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / FS / ClientAPI / MyAccount / quotation.pm
1 package FS::ClientAPI::MyAccount::quotation;
2
3 use strict;
4 use FS::Record qw(qsearch qsearchs);
5 use FS::quotation;
6 use FS::quotation_pkg;
7
8 our $DEBUG = 1;
9
10 sub _custoragent_session_custnum {
11   FS::ClientAPI::MyAccount::_custoragent_session_custnum(@_);
12 }
13
14 sub _quotation {
15   # the currently active quotation
16   my $session = shift;
17   my $quotation;
18   if ( my $quotationnum = $session->{'quotationnum'} ) {
19     $quotation = FS::quotation->by_key($quotationnum);
20   } 
21   if ( !$quotation ) {
22     # find the last quotation created through selfservice
23     $quotation = qsearchs( 'quotation', {
24         'custnum'   => $session->{'custnum'},
25         'usernum'   => $FS::CurrentUser::CurrentUser->usernum,
26         'disabled'  => '',
27     }); 
28     warn "found selfservice quotation #". $quotation->quotationnum."\n"
29       if $quotation and $DEBUG;
30   } 
31   if ( !$quotation ) {
32     $quotation = FS::quotation->new({
33         'custnum'   => $session->{'custnum'},
34         'usernum'   => $FS::CurrentUser::CurrentUser->usernum,
35         '_date'     => time,
36     }); 
37     $quotation->insert; # what to do on error? call the police?
38     warn "started new selfservice quotation #". $quotation->quotationnum."\n"
39       if $quotation and $DEBUG;
40   } 
41   $session->{'quotationnum'} = $quotation->quotationnum;
42   return $quotation;
43 }
44
45 =item quotation_info { session }
46
47 Returns a hashref describing the current quotation, containing:
48
49 - "sections", an arrayref containing one section for each billing frequency.
50   Each one will have:
51   - "description"
52   - "subtotal"
53   - "detail_items", an arrayref of detail items, each with:
54     - "pkgnum", the reference number (actually the quotationpkgnum field)
55     - "description", the package name (or tax name)
56     - "quantity"
57     - "amount"
58
59 =cut
60
61 sub quotation_info {
62   my $p = shift;
63
64   my($context, $session, $custnum) = _custoragent_session_custnum($p);
65   return { 'error' => $session } if $context eq 'error';
66
67   my $quotation = _quotation($session);
68   return { 'error' => "No current quotation for this customer" } if !$quotation;
69   warn "quotation_info #".$quotation->quotationnum
70     if $DEBUG;
71
72   # code reuse ftw
73   my $null_escape = sub { @_ };
74   my ($sections) = $quotation->_items_sections(escape => $null_escape);
75   foreach my $section (@$sections) {
76     $section->{'detail_items'} =
77       [ $quotation->_items_pkg('section' => $section, escape_function => $null_escape) ]; 
78   }
79   return { 'error' => '', 'sections' => $sections }
80 }
81
82 =item quotation_print { session, 'format' }
83
84 Renders the quotation. 'format' can be either 'html' or 'pdf'; the resulting
85 hashref will contain 'document' => the HTML or PDF contents.
86
87 =cut
88
89 sub quotation_print {
90   my $p = shift;
91
92   my($context, $session, $custnum) = _custoragent_session_custnum($p);
93   return { 'error' => $session } if $context eq 'error';
94
95   my $quotation = _quotation($session);
96   return { 'error' => "No current quotation for this customer" } if !$quotation;
97   warn "quotation_print #".$quotation->quotationnum
98     if $DEBUG;
99
100   my $format = $p->{'format'}
101    or return { 'error' => "No rendering format specified" };
102
103   my $document;
104   if ($format eq 'html') {
105     $document = $quotation->print_html;
106   } elsif ($format eq 'pdf') {
107     $document = $quotation->print_pdf;
108   }
109   warn "$format, ".length($document)." bytes\n"
110     if $DEBUG;
111   return { 'error' => '', 'document' => $document };
112 }
113
114 =item quotation_add_pkg { session, 'pkgpart', 'quantity', [ location opts ] }
115
116 Adds a package to the user's current quotation. Session info and 'pkgpart' are
117 required. 'quantity' defaults to 1.
118
119 Location can be specified as 'locationnum' to use an existing location, or
120 'address1', 'address2', 'city', 'state', 'zip', 'country' to create a new one,
121 or it will default to the customer's service location.
122
123 =cut
124
125 sub quotation_add_pkg {
126   my $p = shift;
127
128   my($context, $session, $custnum) = _custoragent_session_custnum($p);
129   return { 'error' => $session } if $context eq 'error';
130   
131   my $quotation = _quotation($session);
132   my $cust_main = $quotation->cust_main;
133
134   my $pkgpart = $p->{'pkgpart'};
135   my $allowed_pkgpart = $cust_main->agent->pkgpart_hashref;
136
137   my $part_pkg = FS::part_pkg->by_key($pkgpart);
138
139   if (!$part_pkg or !$allowed_pkgpart->{$pkgpart}) {
140     warn "disallowed quotation_pkg pkgpart $pkgpart\n"
141       if $DEBUG;
142     return { 'error' => "unknown package $pkgpart" };
143   }
144
145   warn "creating quotation_pkg with pkgpart $pkgpart\n"
146     if $DEBUG;
147   my $quotation_pkg = FS::quotation_pkg->new({
148     'quotationnum'  => $quotation->quotationnum,
149     'pkgpart'       => $p->{'pkgpart'},
150     'quantity'      => $p->{'quantity'} || 1,
151   });
152   if ( $p->{locationnum} > 0 ) {
153     $quotation_pkg->set('locationnum', $p->{locationnum});
154   } elsif ( $p->{address1} ) {
155     my $location = FS::cust_location->find_or_insert(
156       'custnum' => $cust_main->custnum,
157       map { $_ => $p->{$_} }
158         qw( address1 address2 city county state zip country )
159     );
160     $quotation_pkg->set('locationnum', $location->locationnum);
161   }
162
163   my $error = $quotation_pkg->insert
164            || $quotation->estimate;
165
166   { 'error'         => $error,
167     'quotationnum'  => $quotation->quotationnum };
168 }
169  
170 =item quotation_remove_pkg { session, 'pkgnum' }
171
172 Removes the package from the user's current quotation. 'pkgnum' is required.
173
174 =cut
175
176 sub quotation_remove_pkg {
177   my $p = shift;
178
179   my($context, $session, $custnum) = _custoragent_session_custnum($p);
180   return { 'error' => $session } if $context eq 'error';
181   
182   my $quotation = _quotation($session);
183   my $quotationpkgnum = $p->{pkgnum};
184   my $quotation_pkg = FS::quotation_pkg->by_key($quotationpkgnum);
185   if (!$quotation_pkg
186       or $quotation_pkg->quotationnum != $quotation->quotationnum) {
187     return { 'error' => "unknown quotation item $quotationpkgnum" };
188   }
189   warn "removing quotation_pkg with pkgpart ".$quotation_pkg->pkgpart."\n"
190     if $DEBUG;
191
192   my $error = $quotation_pkg->delete
193            || $quotation->estimate;
194
195   { 'error'         => $error,
196     'quotationnum'  => $quotation->quotationnum };
197 }
198
199 =item quotation_order
200
201 Convert the current quotation to a package order.
202
203 =cut
204
205 sub quotation_order {
206   my $p = shift;
207
208   my($context, $session, $custnum) = _custoragent_session_custnum($p);
209   return { 'error' => $session } if $context eq 'error';
210   
211   my $quotation = _quotation($session);
212
213   my $error = $quotation->order;
214   $quotation->set('disabled' => 'Y');
215   $error ||= $quotation->replace;
216
217   return { 'error' => $error };
218 }
219
220 1;