so Search.tsf and Search.rdf work
[freeside.git] / FS / FS / svc_Common.pm
index a223266..80d5e21 100644 (file)
@@ -6,6 +6,7 @@ use FS::Record qw( qsearch qsearchs fields dbh );
 use FS::cust_svc;
 use FS::part_svc;
 use FS::queue;
+use FS::cust_main;
 
 @ISA = qw( FS::Record );
 
@@ -100,8 +101,11 @@ If I<jobnum> is set to an array reference, the jobnums of any export jobs will
 be added to the referenced array.
 
 If I<child_objects> is set to an array reference of FS::tablename objects (for
-example, FS::acct_snarf objects), they will have their svcnum fieldsset and
-will be inserted after this record, but before any exports are run.
+example, FS::acct_snarf objects), they will have their svcnum field set and
+will be inserted after this record, but before any exports are run.  Each
+element of the array can also optionally be a two-element array reference
+containing the child object and the name of an alternate field to be filled in
+with the newly-inserted svcnum, for example C<[ $svc_forward, 'srcsvc' ]>
 
 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
 jobnums), all provisioning jobs will have a dependancy on the supplied
@@ -172,8 +176,15 @@ sub insert {
   }
 
   foreach my $object ( @$objects ) {
-    $object->svcnum($self->svcnum);
-    $error = $object->insert;
+    my($field, $obj);
+    if ( ref($object) eq 'ARRAY' ) {
+      ($obj, $field) = @$object;
+    } else {
+      $obj = $object;
+      $field = 'svcnum';
+    }
+    $obj->$field($self->svcnum);
+    $error = $obj->insert;
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error;
@@ -302,14 +313,52 @@ sub replace {
 
   #new-style exports!
   unless ( $noexport_hack ) {
-    foreach my $part_export ( $new->cust_svc->part_svc->part_export ) {
-      my $error = $part_export->export_replace($new,$old);
+
+    #not quite false laziness, but same pattern as FS::svc_acct::replace and
+    #FS::part_export::sqlradius::_export_replace.  List::Compare or something
+    #would be useful but too much of a pain in the ass to deploy
+
+    my @old_part_export = $old->cust_svc->part_svc->part_export;
+    my %old_exportnum = map { $_->exportnum => 1 } @old_part_export;
+    my @new_part_export = 
+      $new->svcpart
+        ? qsearchs('part_svc', { svcpart=>$new->svcpart } )->part_export
+        : $new->cust_svc->part_svc->part_export;
+    my %new_exportnum = map { $_->exportnum => 1 } @new_part_export;
+
+    foreach my $delete_part_export (
+      grep { ! $new_exportnum{$_->exportnum} } @old_part_export
+    ) {
+      my $error = $delete_part_export->export_delete($old);
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;
-        return "error exporting to ". $part_export->exporttype.
+        return "error deleting, export to ". $delete_part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+
+    foreach my $replace_part_export (
+      grep { $old_exportnum{$_->exportnum} } @new_part_export
+    ) {
+      my $error = $replace_part_export->export_replace($new,$old);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error exporting to ". $replace_part_export->exporttype.
                " (transaction rolled back): $error";
       }
     }
+
+    foreach my $insert_part_export (
+      grep { ! $old_exportnum{$_->exportnum} } @new_part_export
+    ) {
+      my $error = $insert_part_export->export_insert($new);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error inserting export to ". $insert_part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -356,12 +405,12 @@ sub setx {
 
   #get part_svc
   my $svcpart;
-  if ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
+  if ( $self->get('svcpart') ) {
+    $svcpart = $self->get('svcpart');
+  } elsif ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
     my $cust_svc = $self->cust_svc;
     return "Unknown svcnum" unless $cust_svc; 
     $svcpart = $cust_svc->svcpart;
-  } else {
-    $svcpart = $self->getfield('svcpart');
   }
   my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
   return "Unkonwn svcpart" unless $part_svc;
@@ -498,6 +547,20 @@ sub clone_kludge_unsuspend {
   shift;
 }
 
+=item cust_name
+
+Given a svc_ object that contains fields from cust_main (say, from a
+JOINed search.  See httemplate/search/svc_* for examples), returns the 
+equivalent of "$svc_x->cust_svc->cust_pkg->name" (but much more efficient),
+or "(unlinked)" if this service is not linked to a customer.
+
+=cut
+
+sub cust_name {
+  my $svc_x = shift;
+  $svc_x->custnum ? FS::cust_main::name($svc_x) : '(unlinked)';
+}
+
 =back
 
 =head1 BUGS