-ikano, svc_dsl, dsl_note, and qual on-going implementation, RT7111
authorlevinse <levinse>
Wed, 1 Dec 2010 05:56:10 +0000 (05:56 +0000)
committerlevinse <levinse>
Wed, 1 Dec 2010 05:56:10 +0000 (05:56 +0000)
-add predelete_hook_first and predelete_hook into svc_Common (don't use yet, might change or be removed
soon)
-add new cust_location fields into geocode_Mixin location_hash

FS/FS/Schema.pm
FS/FS/dsl_note.pm
FS/FS/geocode_Mixin.pm
FS/FS/part_export/ikano.pm
FS/FS/qual.pm
FS/FS/svc_Common.pm
FS/FS/svc_dsl.pm

index e482bed..5f78b51 100644 (file)
@@ -1685,7 +1685,7 @@ sub tables_hashref {
         'prospectnum',    'int',     'NULL',     '', '', '',
         'locationnum',    'int',     'NULL',     '', '', '',
        'phonenum',     'varchar', 'NULL',       24, '', '',
-        'exportnum',      'int', '', '', '', '', 
+        'exportnum',      'int', 'NULL', '', '', '', 
         'vendor_qual_id',      'varchar', 'NULL', $char_d, '', '', 
         'status',      'char', '', 1, '', '', 
       ],
@@ -1871,9 +1871,9 @@ sub tables_hashref {
       'columns' => [
         'notenum',           'serial',    '',        '', '', '',
        'svcnum',     'int', '',       '', '', '',
-        'by',              'varchar', 'NULL', $char_d,  '', '',
+        'user',     'varchar', 'NULL', $char_d,  '', '',
         'priority',   'char', 'NULL',       1,  '', '', 
-       'date',     'int', 'NULL',       '', '', '',
+       '_date',     'int', 'NULL',       '', '', '',
        'note',     'text', '',       '', '', '',
       ],
       'primary_key' => 'notenum',
index 886365b..0155cbd 100644 (file)
@@ -34,11 +34,11 @@ FS::Record.  The following fields are currently supported:
 
 =item svcnum - the DSL for this note, see L<FS::svc_dsl>
 
-=item by - export-specific, e.g. note's author or ISP vs. telco/vendor
+=item user - export-specific, e.g. note's author or ISP vs. telco/vendor
 
 =item priority - export-specific, e.g. high priority or not; not used by most
 
-=item date - note date
+=item _date - note date
 
 =item note - the note
 
@@ -105,9 +105,9 @@ sub check {
   my $error = 
     $self->ut_numbern('notenum')
     || $self->ut_foreign_key('svcnum', 'svc_dsl', 'svcnum')
-    || $self->ut_textn('by')
+    || $self->ut_textn('user')
     || $self->ut_alphasn('priority')
-    || $self->ut_numbern('date')
+    || $self->ut_numbern('_date')
     || $self->ut_text('note')
   ;
   return $error if $error;
index c153914..d784b57 100644 (file)
@@ -51,7 +51,8 @@ sub location_hash {
   map { my $method = ($_ eq 'geocode') ? $_ : $prefix.$_;
         $_ => $self->get($method);
       }
-      qw( address1 address2 city county state zip country geocode );
+      qw( address1 address2 city county state zip country geocode 
+       location_type location_number location_kind );
 }
 
 =item location_label [ OPTION => VALUE ... ]
index 73f962c..6316b29 100644 (file)
@@ -49,10 +49,18 @@ sub dsl_pull {
 # current assumptions of what won't change (from their side):
 # vendor_order_id, vendor_qual_id, vendor_order_type, pushed, monitored,
 # last_pull, address (from qual), contact info, ProductCustomId
-    my($self, $svc_dsl) = (shift, shift);
+    my($self, $svc_dsl, $threshold) = (shift, shift, shift);
     $self->loadmod;
     my $result = $self->valid_order($svc_dsl,'pull');
     return $result unless $result eq '';
+
+    my $now = time;
+    if($now - $svc_dsl->last_pull < $threshold) {
+       warn "$me skipping pull since threshold not reached (svcnum="
+           . $svc_dsl->svcnum . ",now=$now,threshold=$threshold,last_pull="
+           . $svc_dsl->last_pull .")" if $self->option('debug');
+       return '';
+    }
   
     $result = $self->ikano_command('ORDERSTATUS', 
        { OrderId => $svc_dsl->vendor_order_id } ); 
@@ -242,14 +250,68 @@ sub ikano2fsnote {
 
     new FS::dsl_note( {
        'svcnum' => $svcnum,
-       'by' => $by,
+       'user' => $by,
        'priority' => $n->{'HighPriority'} eq 'false' ? 'N' : 'H',
-       'date' => int(str2time($n->{'Date'})),
+       '_date' => int(str2time($n->{'Date'})),
        'note' => $n->{'Text'},
      } );
 }
 
 sub qual {
+    my($self,$qual) = (shift,shift);
+# address always required for Ikano qual, TN optional (assume dry if not given)
+
+    my %location_hash = $qual->location; 
+    return 'No address provided' unless %location_hash;
+    my $svctn = $qual->phonenum;
+
+    my $result = $self->ikano_command('PREQUAL',
+      { AddressLine1 => $location_hash{address1},
+       AddressUnitType => $location_hash{location_type},
+       AddressUnitValue => $location_hash{location_number},
+       AddressCity => $location_hash{city},
+       AddressState => $location_hash{state},
+       ZipCode => $location_hash{zip},
+       Country => $location_hash{country},
+       LocationType => $location_hash{location_kind},
+       PhoneNumber => length($svctn) > 1 ? $svctn : "STANDALONE",
+       RequestClientIP => '127.0.0.1',
+       CheckNetworks => $self->option('check_networks'),
+      } ); 
+    return 'Invalid prequal response' unless defined $result->{'PrequalId'};
+
+    my $qoptions = {};
+    # lame data structure traversal...
+    # don't spend much time here, just get TermsId and ProductCustomId
+    my $networks = $result->{'Network'};
+    my @networks = defined $networks ? @$networks : ();
+    my $netcount = 0;
+    foreach my $network ( @networks ) { 
+       my $productgroups = $network->{'ProductGroup'};
+       my @productgroups = defined $productgroups ? @$productgroups : ();
+       my $pgcount = 0;
+       foreach my $productgroup ( @productgroups ) {
+           my $prefix = "ikano_Network_$netcount"."_ProductGroup_$pgcount"."_";
+           $qoptions->{$prefix."TermsId"} = $productgroup->{'TermsId'};
+           my $products = $network->{'Product'};
+           my @products = defined $products ? @$products : ();
+           my $prodcount = 0;
+           foreach my $product ( @products ) {
+               $qoptions->{$prefix."Product_$prodcount"."_ProductCustomId"} = $product->{'ProductCustomId'};
+               $prodcount++;
+           }
+           $pgcount++;
+       }
+       $netcount++;
+    }
+
+    {  'vendor_qual_id' => $result->{'PrequalId'},
+       'status' => scalar(@networks) ? 'Q' : 'D',
+       'options' => $qoptions,
+    };
+}
+
+sub qual_html {
     '';
 }
 
@@ -469,8 +531,14 @@ sub _export_delete {
        my $result = $self->ikano_command('CANCEL', 
            { OrderId => $svc_dsl->vendor_order_id, } );
        return $result unless ref($result); # scalar (string) is an error
-
-       return $self->dsl_pull($svc_dsl);
+       return 'Unable to cancel order' unless $result->{'Order'};
+       $result = $result->{'Order'};
+       return 'Invalid cancellation response' 
+           unless $result->{'Status'} eq 'CANCELLED' 
+               && $result->{'OrderId'} eq $svc_dsl->vendor_order_id;
+
+       # we're supposed to do a pull, but it will break everything, so don't
+       # this is very wrong...
     }
     else {
        return "Cannot cancel a NEW order unless it's in NEW or PENDING status";
index de06334..553de13 100644 (file)
@@ -38,10 +38,6 @@ FS::Record.  The following fields are currently supported:
 
 =item locationnum
 
-Either one of these cases must be true:
--locationnum is non-null and prospectnum is null and custnum is null
--locationnum is null and (prospectnum is non-null or custnum is non-null, but not both non-null)
-
 =item phonenum - Service Telephone Number
 
 =item exportnum - export instance providing service-qualification capabilities,
@@ -117,25 +113,40 @@ sub check {
     || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum')
     || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum')
     || $self->ut_numbern('phonenum')
-    || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
+    || $self->ut_foreign_keyn('exportnum', 'part_export', 'exportnum')
     || $self->ut_textn('vendor_qual_id')
     || $self->ut_alpha('status')
   ;
   return $error if $error;
 
-#Either one of these cases must be true:
-#1. locationnum is non-null and prospectnum is null and custnum is null
-#2. locationnum is null and (prospectnum is non-null or custnum is non-null, but not both non-null)
-  return "Invalid prospect/customer/location combination" unless (
-    ( $self->locationnum && !$self->prospectcnum && !$self->custnum  ) #1
- ||
-    ( !$self->locationnum && ( $self->prospectnum || $self->custnum ) 
-       && !( $self->custnum && $self->prospectnum )  ) #2
+  return "Invalid prospect/customer/location combination" if (
+   ( $self->locationnum && $self->prospectnum && $self->custnum ) ||
+   ( !$self->locationnum && !$self->prospectnum && !$self->custnum )
   );
 
   $self->SUPER::check;
 }
 
+sub location {
+    my $self = shift;
+    if ( $self->locationnum ) {
+       my $l = qsearchs( 'cust_location', 
+                   { 'locationnum' => $self->locationnum });
+       return $l->location_hash if $l;
+    }
+    if ( $self->custnum ) {
+       my $c = qsearchs( 'cust_main', { 'custnum' => $self->custnum });
+       return $c->location_hash if $c;
+    }
+  # prospectnum does not imply any particular address! must specify locationnum
+
+    '';
+}
+
+sub status_long {
+    
+}
+
 =back
 
 =head1 SEE ALSO
index e0f1e33..b377642 100644 (file)
@@ -330,6 +330,8 @@ sub preinsert_hook_first { ''; }
 sub _check_duplcate { ''; }
 sub preinsert_hook { ''; }
 sub table_dupcheck_fields { (); }
+sub predelete_hook { ''; }
+sub predelete_hook_first { ''; }
 
 =item delete [ , OPTION => VALUE ... ]
 
@@ -356,9 +358,11 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $error =    $self->SUPER::delete
+  my $error =  $self->predelete_hook_first 
+             || $self->SUPER::delete
               || $self->export('delete', @$export_args)
              || $self->return_inventory
+             || $self->predelete_hook
              || $self->cust_svc->delete
   ;
   if ( $error ) {
index 93d3570..c5557ec 100644 (file)
@@ -263,6 +263,27 @@ sub check {
   $self->SUPER::check;
 }
 
+sub predelete_hook_first {
+    my $self = shift;
+    my @exports = $self->part_svc->part_export_dsl_pull;
+    return 'More than one DSL-pulling export attached' if scalar(@exports) > 1;
+    if ( scalar(@exports) == 1 ) {
+       my $export = $exports[0];
+       return $export->dsl_pull($self);
+    }
+    '';
+}
+
+sub predelete_hook {
+    my $self = shift;
+    my @notes = $self->notes;
+    foreach my $note ( @notes ) {
+       my $error = $note->delete;
+       return $error if $error;
+    }
+    '';
+}
+
 =back
 
 =head1 SEE ALSO