From ad17d8651adb39fdef45f509490a6b2c6eac8301 Mon Sep 17 00:00:00 2001 From: levinse Date: Wed, 1 Dec 2010 05:56:10 +0000 Subject: [PATCH] -ikano, svc_dsl, dsl_note, and qual on-going implementation, RT7111 -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 | 6 ++-- FS/FS/dsl_note.pm | 8 ++--- FS/FS/geocode_Mixin.pm | 3 +- FS/FS/part_export/ikano.pm | 78 +++++++++++++++++++++++++++++++++++++++++++--- FS/FS/qual.pm | 37 ++++++++++++++-------- FS/FS/svc_Common.pm | 6 +++- FS/FS/svc_dsl.pm | 21 +++++++++++++ 7 files changed, 132 insertions(+), 27 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index e482bedfa..5f78b51f8 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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', diff --git a/FS/FS/dsl_note.pm b/FS/FS/dsl_note.pm index 886365b68..0155cbd11 100644 --- a/FS/FS/dsl_note.pm +++ b/FS/FS/dsl_note.pm @@ -34,11 +34,11 @@ FS::Record. The following fields are currently supported: =item svcnum - the DSL for this note, see L -=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; diff --git a/FS/FS/geocode_Mixin.pm b/FS/FS/geocode_Mixin.pm index c153914f0..d784b5709 100644 --- a/FS/FS/geocode_Mixin.pm +++ b/FS/FS/geocode_Mixin.pm @@ -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 ... ] diff --git a/FS/FS/part_export/ikano.pm b/FS/FS/part_export/ikano.pm index 73f962c2c..6316b298d 100644 --- a/FS/FS/part_export/ikano.pm +++ b/FS/FS/part_export/ikano.pm @@ -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"; diff --git a/FS/FS/qual.pm b/FS/FS/qual.pm index de0633423..553de136a 100644 --- a/FS/FS/qual.pm +++ b/FS/FS/qual.pm @@ -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 diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm index e0f1e3338..b377642c7 100644 --- a/FS/FS/svc_Common.pm +++ b/FS/FS/svc_Common.pm @@ -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 ) { diff --git a/FS/FS/svc_dsl.pm b/FS/FS/svc_dsl.pm index 93d357042..c5557ec6f 100644 --- a/FS/FS/svc_dsl.pm +++ b/FS/FS/svc_dsl.pm @@ -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 -- 2.11.0