1 package FS::part_export::ikano;
3 use vars qw(@ISA %info %orderType %orderStatus %loopType $DEBUG $me);
5 use Date::Format qw( time2str );
6 use FS::Record qw(qsearch qsearchs);
11 @ISA = qw(FS::part_export);
13 $me= '[' . __PACKAGE__ . ']';
15 tie my %options, 'Tie::IxHash',
16 'keyid' => { label=>'Ikano keyid' },
17 'username' => { label=>'Ikano username',
20 'password' => { label=>'Ikano password' },
21 'check_networks' => { label => 'Check Networks',
22 default => 'ATT,BELLCA',
28 'desc' => 'Provision DSL to Ikano',
29 'options' => \%options,
31 Requires installation of
32 <a href="http://search.cpan.org/dist/Net-Ikano">Net::Ikano</a> from CPAN.
36 %orderType = ( 'N' => 'NEW', 'X' => 'CANCEL', 'C' => 'CHANGE' );
37 %orderStatus = ('N' => 'NEW',
42 %loopType = ( '' => 'Line-share', '0' => 'Standalone' );
44 sub rebless { shift; }
58 sub loop_type_long { # sub, not a method
59 my($svc_dsl) = (shift);
60 return $loopType{$svc_dsl->loop_type};
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} . ")";
71 my( $self, $command, $args ) = @_;
73 eval "use Net::Ikano;";
76 my $ikano = Net::Ikano->new(
77 'keyid' => $self->option('keyid'),
78 'username' => $self->option('username'),
79 'password' => $self->option('password'),
81 #'reqpreviewonly' => 1,
84 $ikano->$command($args);
88 my( $self, $svc_dsl, $action ) = (shift, shift, shift);
90 warn "$me valid_order action=$action svc_dsl:\n". Dumper($svc_dsl) if $DEBUG;
92 # common to all order types/status/loop_type
93 my $error = !($svc_dsl->desired_due_date
94 && defined $orderType{$svc_dsl->vendor_order_type}
97 && defined $svc_dsl->loop_type
98 && $svc_dsl->vendor_qual_id
100 return 'Missing or invalid order data' if $error;
102 return 'Package does not have an external id configured'
103 if $svc_dsl->cust_svc->cust_pkg->part_pkg->options('externalid',1) eq '';
105 return 'No valid qualification for this order'
106 unless qsearch( 'qual', { 'vendor_qual_id' => $svc_dsl->vendor_qual_id });
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) {
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->phonenum eq '' && $svc_dsl->loop_type eq '0') # dry
118 || ($svc_dsl->phonenum ne '' && $svc_dsl->loop_type eq '') # line-share
121 return 'Invalid order data' if $error;
124 elsif($svc_dsl->vendor_order_type eq 'X') {
126 elsif($svc_dsl->vendor_order_type eq 'C') {
133 my ($self,$vendor_qual_id,$ProductCustomId) = (shift,shift,shift);
134 my $qual = qsearchs( 'qual', { 'vendor_qual_id' => $vendor_qual_id });
135 return '' unless $qual;
136 my %qual_options = $qual->options;
137 while (($optionname, $optionvalue) = each %qual_options) {
138 if ( $optionname =~ /^ikano_Network_(\d+)_ProductGroup_(\d+)_Product_(\d+)_ProductCustomId$/
139 && $optionvalue eq $ProductCustomId ) {
141 my $productgroup = $2;
142 return $qual->option("ikano_Network_".$network."_ProductGroup_".$productgroup."_TermsId");
148 sub orderstatus_long2short {
149 my ($self,$order_status) = (shift,shift);
150 while (($k, $v) = each %orderStatus) {
151 return $k if $v eq $order_status;
157 my( $self, $svc_dsl ) = (shift, shift);
159 my $result = $self->valid_order($svc_dsl,'insert');
160 return $result unless $result eq '';
162 my $isp_chg = $svc_dsl->isp_chg eq 'Y' ? 'YES' : 'NO';
163 my $contactTN = $svc_dsl->cust_svc->cust_pkg->cust_main->daytime;
164 $contactTN =~ s/[^0-9]//g;
166 my $ProductCustomId = $svc_dsl->cust_svc->cust_pkg->part_pkg->option('externalid',1);
170 ProductCustomId => $ProductCustomId,
171 TermsId => $self->qual2termsid($svc_dsl->vendor_qual_id,$ProductCustomId),
172 DSLPhoneNumber => $svc_dsl->loop_type eq '0' ? 'STANDALONE'
173 : $svc_dsl->phonenum,
174 Password => $svc_dsl->password,
175 PrequalId => $svc_dsl->vendor_qual_id,
176 CompanyName => $svc_dsl->company,
177 FirstName => $svc_dsl->first,
178 LastName => $svc_dsl->last,
180 ContactMethod => 'PHONE',
181 ContactPhoneNumber => $contactTN,
182 ContactEmail => 'x@x.xx',
184 DateToOrder => time2str("%Y-%m-%d",$svc_dsl->desired_due_date),
185 RequestClientIP => '127.0.0.1',
186 IspChange => $isp_chg,
187 IspPrevious => $isp_chg eq 'YES' ? $svc_dsl->isp_prev : '',
188 CurrentProvider => $isp_chg eq 'NO' ? $svc_dsl->isp_prev : '',
191 $result = $self->ikano_command('ORDER',$args);
192 return $result unless ref($result); # scalar (string) is an error
194 # now we're getting an OrderResponse which should have one Order in it
195 warn "$me _export_insert OrderResponse hash:\n".Dumper($result) if $DEBUG;
197 return 'Invalid order response' unless defined $result->{'Order'};
198 $result = $result->{'Order'};
200 return 'No order id or status returned'
201 unless defined $result->{'Status'} && defined $result->{'OrderId'};
203 $svc_dsl->pushed(time);
204 $svc_dsl->last_pull((time)+1);
205 $svc_dsl->vendor_order_id($result->{'OrderId'});
206 $svc_dsl->vendor_order_status($self->orderstatus_long2short($result->{'Status'}));
207 $svc_dsl->username($result->{'Username'});
208 local $FS::svc_Common::noexport_hack = 1;
209 local $FS::UID::AutoCommit = 0;
210 $result = $svc_dsl->replace;
211 return 'Error setting DSL fields' if $result;
215 sub _export_replace {
216 my( $self, $new, $old ) = (shift, shift, shift);
221 my( $self, $svc_dsl ) = (shift, shift);
225 sub _export_suspend {
226 my( $self, $svc_dsl ) = (shift, shift);
230 sub _export_unsuspend {
231 my( $self, $svc_dsl ) = (shift, shift);