fc86ba3bc3751c9739fc77ff5f909520bc013738
[freeside.git] / FS / FS / Maestro.pm
1 package FS::Maestro;
2
3 use strict;
4 use Date::Format;
5 use FS::Conf;
6 use FS::Record qw( qsearchs );
7 use FS::cust_main;
8 use FS::cust_pkg;
9 use FS::part_svc;
10
11 sub customer_status {
12   my( $custnum ) = shift; #@_;
13   my $svcnum = @_ ? shift : '';
14
15   my $curuser = $FS::CurrentUser::CurrentUser;
16
17   my $cust_main = qsearchs({
18     'table'     => 'cust_main',
19     'hashref'   => { 'custnum' => $custnum },
20     'extra_sql' => ' AND '. $curuser->agentnums_sql,
21   })
22     or return { 'status' => 'E',
23                 'error'  => "custnum $custnum not found" };
24
25   my( $svc_pbx, $good_till, $outbound_service ) = ( '', '', '' );
26   my %result = ();
27   if ( $svcnum ) {
28    
29     ###
30     # reseller scenario to maestro (customer w/ multiple packages)
31     ###
32
33     # find $svc_pbx
34
35     $svc_pbx = qsearchs({
36       'table'      => 'svc_pbx',
37       'addl_from'  => ' LEFT JOIN cust_svc USING ( svcnum ) '.
38                       ' LEFT JOIN cust_pkg USING ( pkgnum ) ',
39       'hashref'   => { 'svcnum' => $svcnum },
40       'extra_sql' => " AND custnum = $custnum",
41     })
42       or return { 'status' => 'E',
43                   'error'  => "svcnum $svcnum not found" };
44
45     #status in the reseller scenario
46
47     my $cust_pkg = $svc_pbx->cust_svc->cust_pkg;
48
49     $result{'status'} = substr($cust_pkg->ucfirst_status,0,1);
50
51     # find "outbound service" y/n
52
53     #XXX outbound service per-reseller ?
54     #my @cust_pkg = $cust_main->cust_pkg;
55     #
56     #my $conf = new FS::Conf;
57     #my %outbound_pkgs = map { $_=>1 } $conf->config('mc-outbound_packages');
58     #my $outbound_service =
59     #  scalar( grep { $outbound_pkgs{ $_->pkgpart }
60     #                   && !$_->get('cancel')
61     #               }
62     #               @cust_pkg
63     #        )
64     #  ? 1 : 0;
65
66     # find "good till" date/time stamp (this package)
67
68     $good_till  = time2str('%c', $cust_pkg->bill || time );
69
70   } else {
71
72     ###
73     # regular customer to maestro (single package)
74     ###
75
76     my @cust_pkg = $cust_main->cust_pkg;
77
78     #things specific to the non-reseller scenario
79
80     $result{'status'} = substr($cust_main->ucfirst_status,0,1);
81
82     $result{'products'} =
83       [ map $_->pkgpart, grep !$_->get('cancel'), @cust_pkg ];
84
85     #find svc_pbx
86
87     my @cust_svc = map $_->cust_svc, @cust_pkg;
88
89     my @cust_svc_pbx =
90       grep { my($n,$l,$t) = $_->label; $t eq 'svc_pbx' }
91       @cust_svc;
92
93     if ( ! @cust_svc_pbx ) {
94       return { 'status' => 'E',
95                'error'  => "customer $custnum has no conference service" };
96     } elsif ( scalar(@cust_svc_pbx) > 1 ) {
97       return { 'status' => 'E',
98                'error'  =>
99                  "customer $custnum has more than one conference".
100                  " service (reseller?); specify a svcnum as a second argument",
101              };
102     }
103
104     my $cust_svc_pbx = $cust_svc_pbx[0];
105
106     $svc_pbx = $cust_svc_pbx->svc_x;
107
108     # find "outbound service" y/n
109
110     my $conf = new FS::Conf;
111     my %outbound_pkgs = map { $_=>1 } $conf->config('mc-outbound_packages');
112     $outbound_service =
113       scalar( grep { $outbound_pkgs{ $_->pkgpart }
114                        && !$_->get('cancel')
115                    }
116                    @cust_pkg
117             )
118       ? 1 : 0;
119
120     # find "good till" date/time stamp
121
122     my @active_cust_pkg =
123       sort { $a->bill <=> $b->bill }
124       grep { !$_->get('cancel') && $_->part_pkg->freq ne '0' }
125       @cust_pkg;
126     $good_till = time2str('%c', $active_cust_pkg[0]->bill || time );
127
128   }
129
130   return { 
131     'name'   => $cust_main->name,
132     'email'  => $cust_main->invoicing_list_emailonly_scalar,
133     'agentnum' => $cust_main->agentnum,
134     'agent'    => $cust_main->agent->agent,
135     'max_lines'        => $svc_pbx ? $svc_pbx->max_extensions : '',
136     'max_simultaneous' => $svc_pbx ? $svc_pbx->max_simultaneous : '',
137     'outbound_service' => $outbound_service,
138     'good_till' => $good_till,
139     %result,
140   };
141
142 }
143
144 #some false laziness w/ MyAccount order_pkg
145 sub order_pkg {
146   my $opt = ref($_[0]) ? shift : { @_ };
147
148   $opt->{'title'} = delete $opt->{'name'}
149     if !exists($opt->{'title'}) && exists($opt->{'name'});
150
151   my $custnum = $opt->{'custnum'};
152
153   my $curuser = $FS::CurrentUser::CurrentUser;
154
155   my $cust_main = qsearchs({
156     'table'     => 'cust_main',
157     'hashref'   => { 'custnum' => $custnum },
158     'extra_sql' => ' AND '. $curuser->agentnums_sql,
159   })
160     or return { 'error'  => "custnum $custnum not found" };
161
162   my $status = $cust_main->status;
163   #false laziness w/ClientAPI/Signup.pm
164
165   my $cust_pkg = new FS::cust_pkg ( {
166     'custnum' => $custnum,
167     'pkgpart' => $opt->{'pkgpart'},
168   } );
169   my $error = $cust_pkg->check;
170   return { 'error' => $error } if $error;
171
172   my @svc = ();
173   unless ( $opt->{'svcpart'} eq 'none' ) {
174
175     my $svcpart = '';
176     if ( $opt->{'svcpart'} =~ /^(\d+)$/ ) {
177       $svcpart = $1;
178     } else {
179       $svcpart = $cust_pkg->part_pkg->svcpart; #($svcdb);
180     }
181
182     my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } );
183     return { 'error' => "Unknown svcpart $svcpart" } unless $part_svc;
184
185     my $svcdb = $part_svc->svcdb;
186
187     my %fields = (
188       'svc_acct'     => [ qw( username domsvc _password sec_phrase popnum ) ],
189       'svc_domain'   => [ qw( domain ) ],
190       'svc_phone'    => [ qw( phonenum pin sip_password phone_name ) ],
191       'svc_external' => [ qw( id title ) ],
192       'svc_pbx'      => [ qw( id title ) ],
193     );
194   
195     my $svc_x = "FS::$svcdb"->new( {
196       'svcpart'   => $svcpart,
197       map { $_ => $opt->{$_} } @{$fields{$svcdb}}
198     } );
199     
200     #snarf processing not necessary here (or probably at all, anymore)
201     
202     my $y = $svc_x->setdefault; # arguably should be in new method
203     return { 'error' => $y } if $y && !ref($y);
204   
205     $error = $svc_x->check;
206     return { 'error' => $error } if $error;
207
208     push @svc, $svc_x;
209
210   }
211
212   use Tie::RefHash;
213   tie my %hash, 'Tie::RefHash';
214   %hash = ( $cust_pkg => \@svc );
215   #msgcat
216   $error = $cust_main->order_pkgs( \%hash, '', 'noexport' => 1 );
217   return { 'error' => $error } if $error;
218
219 # currently they're using this in the reseller scenario, so don't
220 # bill the package immediately
221 #  my $conf = new FS::Conf;
222 #  if ( $conf->exists('signup_server-realtime') ) {
223 #
224 #    my $bill_error = _do_bop_realtime( $cust_main, $status );
225 #
226 #    if ($bill_error) {
227 #      $cust_pkg->cancel('quiet'=>1);
228 #      return $bill_error;
229 #    } else {
230 #      $cust_pkg->reexport;
231 #    }
232 #
233 #  } else {
234     $cust_pkg->reexport;
235 #  }
236
237   my $svcnum = $svc[0] ? $svc[0]->svcnum : '';
238
239   return { error=>'', pkgnum=>$cust_pkg->pkgnum, svcnum=>$svcnum };
240
241 }
242
243 1;