qual, svc_dsl, and ikano on-going implementation, RT7111
[freeside.git] / FS / FS / part_export / ikano.pm
1 package FS::part_export::ikano;
2
3 use vars qw(@ISA %info %orderType %orderStatus %loopType $DEBUG $me);
4 use Tie::IxHash;
5 use Date::Format qw( time2str );
6 use FS::Record qw(qsearch qsearchs);
7 use FS::part_export;
8 use FS::svc_dsl;
9 use Data::Dumper;
10
11 @ISA = qw(FS::part_export);
12 $DEBUG = 1;
13 $me= '[' .  __PACKAGE__ . ']';
14
15 tie my %options, 'Tie::IxHash',
16   'keyid'         => { label=>'Ikano keyid' },
17   'username'      => { label=>'Ikano username',
18                         default => 'admin',
19                         },
20   'password'      => { label=>'Ikano password' },
21   'check_networks' => { label => 'Check Networks',
22                     default => 'ATT,BELLCA',
23                     },
24 ;
25
26 %info = (
27   'svc'     => 'svc_dsl',
28   'desc'    => 'Provision DSL to Ikano',
29   'options' => \%options,
30   'notes'   => <<'END'
31 Requires installation of
32 <a href="http://search.cpan.org/dist/Net-Ikano">Net::Ikano</a> from CPAN.
33 END
34 );
35     
36 %orderType = ( 'N' => 'New', 'X' => 'Cancel', 'C' => 'Change' );
37 %orderStatus = ('N' => 'New',
38                 'P' => 'Pending',
39                 'X' => 'Cancelled',
40                 'C' => 'Completed',
41                 'E' => 'Error' );
42 %loopType = ( '' => 'Line-share', '0' => 'Standalone' );
43
44 sub rebless { shift; }
45
46 sub dsl_pull {
47     '';
48 }
49
50 sub dsl_qual {
51     '';
52 }
53
54 sub notes_html {
55     '';
56 }
57
58 sub loop_type_long { # sub, not a method
59     my($svc_dsl) = (shift);
60     return $loopType{$svc_dsl->loop_type};
61 }
62
63 sub status_line {
64     my($self,$svc_dsl) = (shift,shift);
65     return "Ikano ".$orderType{$svc_dsl->vendor_order_type}." order #"
66         . $svc_dsl->vendor_order_id . " (Status: " 
67         . $orderStatus{$svc_dsl->vendor_order_status} . ")";
68 }
69
70 sub ikano_command {
71   my( $self, $command, $args ) = @_;
72
73   eval "use Net::Ikano;";
74   die $@ if $@;
75
76   my $ikano = Net::Ikano->new(
77     'keyid' => $self->option('keyid'),
78     'username'  => $self->option('username'),
79     'password'  => $self->option('password'),
80     'debug'    => 1,
81     #'reqpreviewonly' => 1,
82   );
83
84   $ikano->$command($args);
85 }
86
87 sub valid_order {
88   my( $self, $svc_dsl, $action ) = (shift, shift, shift);
89   
90   warn "$me valid_order action=$action svc_dsl: ". Dumper($svc_dsl) if $DEBUG;
91
92   # common to all order types/status/loop_type
93   my $error = !($svc_dsl->desired_dd 
94             &&  defined $orderType{$svc_dsl->vendor_order_type}
95             &&  $svc_dsl->first
96             &&  $svc_dsl->last
97             &&  defined $svc_dsl->loop_type
98             &&  $svc_dsl->vendor_qual_id
99             );
100   return 'Missing or invalid order data' if $error;
101   
102   return 'Package does not have an external id configured'
103     if $svc_dsl->cust_svc->cust_pkg->part_pkg->options('externalid',1) eq '';
104
105   return 'No valid qualification for this order' 
106     unless qsearch( 'qual', { 'vendor_qual_id' => $svc_dsl->vendor_qual_id });
107
108   # now go by order type
109   # weird ifs & long lines for readability and ease of understanding - don't change
110   if($svc_dsl->vendor_order_type eq 'N') {
111     if($svc_dsl->pushed) {
112     }
113     else { # unpushed New order - cannot do anything other than push it
114         $error = !($action eq 'insert'
115             &&  length($svc_dsl->vendor_order_id) < 1
116             &&  length($svc_dsl->vendor_order_status) < 1
117             && ( ($svc_dsl->svctn eq '' && $svc_dsl->loop_type eq '0') # dry
118               || ($svc_dsl->svctn ne '' && $svc_dsl->loop_type eq '') # line-share
119                )
120             );  
121         return 'Invalid order data' if $error;
122     }
123   }
124   elsif($svc_dsl->vendor_order_type eq 'X') {
125   }
126   elsif($svc_dsl->vendor_order_type eq 'C') {
127   }
128
129  '';
130 }
131
132 sub qual2termsid {
133     my ($self,$vendor_qual_id) = (shift,shift);
134     my $qual = qsearchs( 'qual', { 'vendor_qual_id' => $vendor_qual_id });
135     return '' unless $qual;
136     my %qual_options = $qual->options;
137     foreach my $optionname ( keys %qual_options ) {
138         if ( $optionname =~ /^ikano_network_(\d+)_productgroup_(\d+)_termsid$/ ) {
139             return $qual_options{$optionname};
140         }
141         # XXX finish this properly - the above is wrong
142     }
143     '';
144 }
145
146 sub _export_insert {
147   my( $self, $svc_dsl ) = (shift, shift);
148
149   my $result = $self->valid_order($svc_dsl,'insert');
150   return $result unless $result eq '';
151
152   my $isp_chg = $svc_dsl->isp_chg eq 'Y' ? 'YES' : 'NO';
153   my $contactTN = $svc_dsl->cust_svc->cust_pkg->cust_main->daytime;
154   $contactTN =~ s/[^0-9]//g;
155
156   my $args = {
157         orderType => 'NEW',
158         ProductCustomId => 
159             $svc_dsl->cust_svc->cust_pkg->part_pkg->option('externalid',1),
160         TermsId => $self->qual2termsid($svc_dsl->vendor_qual_id),
161         DSLPhoneNumber => $svc_dsl->loop_type eq '0' ? 'STANDALONE'
162                                                     : $svc_dsl->svctn,
163         Password => $svc_dsl->password,
164         PrequalId => $svc_dsl->vendor_qual_id,
165         CompanyName => $svc_dsl->company,
166         FirstName => $svc_dsl->first,
167         LastName => $svc_dsl->last,
168         MiddleName => '',
169         ContactMethod => 'PHONE',
170         ContactPhoneNumber => $contactTN,
171         ContactEmail => 'x@x.xx',
172         ContactFax => '',
173         DateToOrder => time2str("%Y-%m-%d",$svc_dsl->desired_dd),
174         RequestClientIP => '127.0.0.1',
175         IspChange => $isp_chg,
176         IspPrevious => $isp_chg eq 'YES' ? $svc_dsl->isp_prev : '',
177         CurrentProvider => $isp_chg eq 'NO' ? $svc_dsl->isp_prev : '',
178   };
179
180   $result = $self->ikano_command('ORDER',$args); 
181   return $result unless ref($result); # scalar (string) is an error
182
183   # now we're getting an OrderResponse which should have one Order in it
184   warn Dumper($result) if $DEBUG;
185   my ($pushed,$vendor_order_id,$vendor_order_status,$last_pull);
186   $pushed = time;
187   $last_pull = time;
188   $last_pull++;
189   
190   return 'Invalid order response' unless defined $result->{'Order'};
191   $result = $result->{'Order'};
192
193   return 'No order id or status returned' 
194     unless defined $result->{'Status'} && defined $result->{'OrderId'};
195
196   $vendor_order_id = $result->{'OrderId'};
197   $vendor_order_status = $result->{'Status'};
198       
199 # XXX we need to set all of these values (everything in the last my statement
200 # above) in the svc without:
201 # a. re-triggering exports
202 # b. committing the svc into the db now (because other things in the caller
203 #  and further up the stack may decide that the svc shouldn't be inserted)
204
205   return '';
206 }
207
208 sub _export_replace {
209   my( $self, $new, $old ) = (shift, shift, shift);
210   '';
211 }
212
213 sub _export_delete {
214   my( $self, $svc_dsl ) = (shift, shift);
215   '';
216 }
217
218 sub _export_suspend {
219   my( $self, $svc_dsl ) = (shift, shift);
220   '';
221 }
222
223 sub _export_unsuspend {
224   my( $self, $svc_dsl ) = (shift, shift);
225   '';
226 }
227
228 1;