From 1594e450be1c4f0c9f9a2d98ab72c714a4695bdc Mon Sep 17 00:00:00 2001 From: levinse Date: Mon, 29 Nov 2010 02:30:39 +0000 Subject: [PATCH] move DSL notes into a dsl_note table, implement partial ikano dsl_pull --- FS/FS.pm | 2 + FS/FS/Mason.pm | 1 + FS/FS/Schema.pm | 16 ++++- FS/FS/dsl_note.pm | 127 ++++++++++++++++++++++++++++++++++ FS/FS/part_export/ikano.pm | 162 ++++++++++++++++++++++++++++++++++++++++++-- FS/FS/svc_dsl.pm | 16 +++-- FS/MANIFEST | 2 + FS/t/dsl_note.t | 5 ++ httemplate/view/svc_dsl.cgi | 2 +- 9 files changed, 319 insertions(+), 14 deletions(-) create mode 100644 FS/FS/dsl_note.pm create mode 100644 FS/t/dsl_note.t diff --git a/FS/FS.pm b/FS/FS.pm index 668af0ad0..f9061f1d2 100644 --- a/FS/FS.pm +++ b/FS/FS.pm @@ -150,6 +150,8 @@ L - DSL, wireless and other broadband class. L - DSL +L - DSL order notes + L - Address block class L - Router class diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index cb8130f73..c13207474 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -261,6 +261,7 @@ if ( -e $addl_handler_use_file ) { use FS::svc_dsl; use FS::qual; use FS::qual_option; + use FS::dsl_note; # Sammath Naur if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index e95e1f704..281ca6ba1 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1861,13 +1861,25 @@ sub tables_hashref { 'staticips', 'text', 'NULL', '', '', '', 'monitored', 'char', 'NULL', 1, '', '', 'last_pull', 'int', 'NULL', '', '', '', - 'notes', 'text', 'NULL', '', '', '', ], 'primary_key' => 'svcnum', 'unique' => [ ], 'index' => [ ['phonenum'], ['vendor_order_id'] ], }, - + + 'dsl_note' => { + 'columns' => [ + 'notenum', 'serial', '', '', '', '', + 'svcnum', 'int', '', '', '', '', + 'by', 'varchar', 'NULL', $char_d, '', '', + 'priority', 'char', 'NULL', 1, '', '', + 'date', 'int', 'NULL', '', '', '', + 'note', 'text', '', '', '', '', + ], + 'primary_key' => 'notenum', + 'unique' => [ ], + 'index' => [ ['svcnum'] ], + }, 'domain_record' => { 'columns' => [ diff --git a/FS/FS/dsl_note.pm b/FS/FS/dsl_note.pm new file mode 100644 index 000000000..886365b68 --- /dev/null +++ b/FS/FS/dsl_note.pm @@ -0,0 +1,127 @@ +package FS::dsl_note; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::dsl_note - Object methods for dsl_note records + +=head1 SYNOPSIS + + use FS::dsl_note; + + $record = new FS::dsl_note \%hash; + $record = new FS::dsl_note { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::dsl_note object represents a DSL order note. FS::dsl_note inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item notenum - primary key + +=item svcnum - the DSL for this note, see L + +=item by - 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 note - the note + + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new note. To add the note to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'dsl_note'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid note. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('notenum') + || $self->ut_foreign_key('svcnum', 'svc_dsl', 'svcnum') + || $self->ut_textn('by') + || $self->ut_alphasn('priority') + || $self->ut_numbern('date') + || $self->ut_text('note') + ; + return $error if $error; + + $self->SUPER::check; +} + +=back + +=head1 SEE ALSO + +L, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/FS/part_export/ikano.pm b/FS/FS/part_export/ikano.pm index 50fa29221..f162f2523 100644 --- a/FS/FS/part_export/ikano.pm +++ b/FS/FS/part_export/ikano.pm @@ -3,6 +3,7 @@ package FS::part_export::ikano; use vars qw(@ISA %info %orderType %orderStatus %loopType $DEBUG $me); use Tie::IxHash; use Date::Format qw( time2str ); +use Date::Parse qw( str2time ); use FS::Record qw(qsearch qsearchs); use FS::part_export; use FS::svc_dsl; @@ -44,10 +45,157 @@ END sub rebless { shift; } sub dsl_pull { +# we distinguish between invalid new data (return error) versus data that +# has legitimately changed (may eventually execute hooks; now just update) + + my($self, $svc_dsl) = (shift, shift); + my $result = $self->valid_order($svc_dsl,'pull'); + return $result unless $result eq ''; + + $result = $self->ikano_command('ORDERSTATUS', + { OrderId => $svc_dsl->vendor_order_id } ); + return $result unless ref($result); # scalar (string) is an error + + # now we're getting an OrderResponse which should have one Order in it + warn "$me pull OrderResponse hash:\n".Dumper($result) if $DEBUG; + + return 'Invalid order response' unless defined $result->{'Order'}; + $result = $result->{'Order'}; + + return 'No order id or status returned' + unless defined $result->{'Status'} && defined $result->{'OrderId'}; + + # update this always (except in the above cases), even if error later + $svc_dsl->last_pull((time)); + local $FS::svc_Common::noexport_hack = 1; + local $FS::UID::AutoCommit = 1; + my $error = $svc_dsl->replace; + return 'Error updating last pull time' if $error; + + # let's compare what we have to what we got back... + + # 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 $wechanged = 0; + + # 1. status + my $new_order_status = $self->orderstatus_long2short($result->{'Status'}); + return 'Invalid new status' if $new_order_status eq ''; + if($svc_dsl->vendor_order_status ne $new_order_status) { + if($new_order_status eq 'X' || $new_order_status eq 'C') { + $svc_dsl->monitored(''); + } + $svc_dsl->vendor_order_status($new_order_status); + $wechanged = 1; + } + + # 2. fields we don't care much about + my %justUpdate = ( 'first' => 'FirstName', + 'last' => 'LastName', + 'company' => 'CompanyName', + 'username' => 'Username', + 'password' => 'Password' ); + + while (($fsf, $ikanof) = each %justUpdate) { + if ( $result->{$ikanof} ne $svc_dsl->$fsf ) { + $svc_dsl->$fsf($result->{$ikanof}); + $wechanged = 1; + } + } + + # let's look inside the response element + my @product = $result->{'Product'}; + return 'Invalid number of products on order' if scalar(@product) != 1; + my $product = $result->{'Product'}[0]; + + # 3. phonenum + if($svc_dsl->loop_type eq '') { # line-share + # TN may change only if sub changes it and + # New or Change order in Completed status + my $tn = $product->{'PhoneNumber'}; + if($tn ne $svc_dsl->phonenum) { + if( ($svc_dsl->vendor_order_type eq 'N' + || $svc_dsl->vendor_order_type eq 'C') + && $svc_dsl->vendor_order_status eq 'C' ) { + $svc_dsl->phonenum($tn); + $wechanged = 1; + } + else { return 'TN has changed in an invalid state'; } + } + } + elsif($svc_dsl->loop_type eq '0') { # dry loop + return 'Invalid PhoneNumber value for a dry loop' + if $product->{'PhoneNumber'} ne 'STANDALONE'; + my $tn = $product->{'VirtualPhoneNumber'}; + if($tn ne $svc_dsl->phonenum) { + if( ($svc_dsl->vendor_order_type eq 'N' + || $svc_dsl->vendor_order_type eq 'C') + && $svc_dsl->vendor_order_status ne 'C' + && $svc_dsl->vendor_order_status ne 'X') { + $svc_dsl->phonenum($tn); + $wechanged = 1; + } + } + } + + # 4. desired_due_date - may change if manually changed + if($svc_dsl->vendor_order_type eq 'N' + || $svc_dsl->vendor_order_type eq 'C'){ + my $f = str2time($product->{'DateToOrder'}); + return 'Invalid DateToOrder' unless $f; + if ( $svc_dsl->desired_due_date != $f ) { + $svc_dsl->desired_due_date($f); + $wechanged = 1; + } + # XXX: optionally sync back to start_date or whatever... + } + elsif($svc_dsl->vendor_order_type eq 'X'){ + my $f = str2time($product->{'DateToDisconnect'}); + return 'Invalid DateToDisconnect' unless $f; + if ( $svc_dsl->desired_due_date != $f ) { + $svc_dsl->desired_due_date($f); + $wechanged = 1; + } + # XXX: optionally sync back to expire or whatever... + } + + # 4. due_date + if($svc_dsl->vendor_order_type eq 'N' + || $svc_dsl->vendor_order_type eq 'C') { + my $f = str2time($product->{'ActivationDate'}); + if($svc_dsl->vendor_order_status ne 'N') { + return 'Invalid ActivationDate' unless $f; + if( $svc_dsl->due_date != $f ) { + $svc_dsl->due_date($f); + $wechanged = 1; + } + } + } + # Ikano API does not implement the proper disconnect date, + # so we can't do anything about it + + # 6. staticips - for now just comma-separate them + my @statics = $result->{'StaticIps'}; +# XXX + + # 7. notes - put them into the common format: + # "by" = 0 for Ikano; 1 for ISP + # "priority" = 1 for high priority; 0 otherwise + my @notes = $result->{'OrderNotes'}; +# XXX + + if($wechanged) { # you can't check ->modified, the replace above screws it + # noexport_hack and AutoCommit are set correctly above + $result = $svc_dsl->replace; + return 'Error updating DSL data' if $result; + } + ''; } -sub dsl_qual { +sub qual { ''; } @@ -109,6 +257,11 @@ sub valid_order { # weird ifs & long lines for readability and ease of understanding - don't change if($svc_dsl->vendor_order_type eq 'N') { if($svc_dsl->pushed) { + $error = !($action eq 'pull' + && length($svc_dsl->vendor_order_id) > 0 + && length($svc_dsl->vendor_order_status) > 0 + ); + return 'Invalid order data' if $error; } else { # unpushed New order - cannot do anything other than push it $error = !($action eq 'insert' @@ -147,10 +300,9 @@ sub qual2termsid { sub orderstatus_long2short { my ($self,$order_status) = (shift,shift); - while (($k, $v) = each %orderStatus) { - return $k if $v eq $order_status; - } - return ''; + my %rorderStatus = reverse %orderStatus; + return $rorderStatus{$order_status} if exists $rorderStatus{$order_status}; + ''; } sub _export_insert { diff --git a/FS/FS/svc_dsl.pm b/FS/FS/svc_dsl.pm index da62dc6bb..2c30b006e 100644 --- a/FS/FS/svc_dsl.pm +++ b/FS/FS/svc_dsl.pm @@ -99,10 +99,6 @@ Ikano-specific fields, do not use otherwise =item last_pull - time of last data pull from vendor/telco -=item notes - -DSL order notes placed by staff or vendor/telco on the vendor/telco order - =back @@ -171,7 +167,6 @@ sub table_info { 'monitored' => { label => 'Monitored', type => 'checkbox', %dis2 }, 'last_pull' => { label => 'Last Pull', type => 'disabled' }, - 'notes' => { label => 'Order Notes', %dis1 }, }, }; } @@ -186,6 +181,16 @@ sub label { return $self->svcnum; } +=item notes + +Returns the set of FS::dsl_notes associated with this service + +=cut +sub notes { + my $self = shift; + qsearch( 'dsl_note', { 'svcnum' => $self->svcnum } ); +} + =item insert Adds this record to the database. If there is an error, returns the error, @@ -250,7 +255,6 @@ sub check { || $self->ut_textn('staticips') || $self->ut_enum('monitored', [ '', 'Y' ]) || $self->ut_numbern('last_pull') - || $self->ut_textn('notes') ; return $error if $error; diff --git a/FS/MANIFEST b/FS/MANIFEST index bd37d7be2..b58b58e20 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -546,3 +546,5 @@ FS/qual.pm t/qual.t FS/qual_option.pm t/qual_option.t +FS/dsl_note.pm +t/dsl_note.t diff --git a/FS/t/dsl_note.t b/FS/t/dsl_note.t new file mode 100644 index 000000000..6fb698743 --- /dev/null +++ b/FS/t/dsl_note.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::dsl_note; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/view/svc_dsl.cgi b/httemplate/view/svc_dsl.cgi index f34e31736..1107d46c8 100644 --- a/httemplate/view/svc_dsl.cgi +++ b/httemplate/view/svc_dsl.cgi @@ -57,6 +57,6 @@ my $svc_cb = sub { # else add any other export-specific stuff here $footer = "".$export->status_line($svc_x).""; - $footer .= "


Order Notes:
".$export->notes_html; + $footer .= "


Order Notes:
".$export->notes_html($svc_x); }; -- 2.11.0