query billing events to see affected objects, #15142
authormark <mark>
Wed, 15 Feb 2012 01:32:10 +0000 (01:32 +0000)
committermark <mark>
Wed, 15 Feb 2012 01:32:10 +0000 (01:32 +0000)
FS/FS/part_event.pm
httemplate/browse/part_event.html
httemplate/search/elements/search-html.html
httemplate/view/cust_pkg.cgi [new file with mode: 0755]
httemplate/view/part_event-targets.html [new file with mode: 0644]

index dfbb6a5..31d2afd 100644 (file)
@@ -253,31 +253,23 @@ sub templatename {
   }
 }
 
-=item initialize PARAMS
-
-Identify all objects eligible for this event and create L<FS::cust_event>
-records for each of them, as of the present time, with status "initial".  When 
-combined with conditions that prevent an event from running more than once
-(at all or within some period), this will exclude any objects that met the 
-conditions before the event was created.
+=item targets
 
-If an L<FS::part_event> object needs to be initialized, it should be created 
-in a disabled state to avoid running the event prematurely for any existing 
-objects.  C<initialize> will enable it once all the cust_event records 
-have been created.
+Returns all objects (of type C<FS::eventtable>, for this object's 
+C<eventtable>) eligible for processing under this event, as of right now.
+The L<FS::cust_event> object used to test event conditions will be 
+included in each object as the 'cust_event' pseudo-field.
 
-This may take some time, so it should be run from the job queue.
+This is not used in normal event processing (which is done on a 
+per-customer basis to control timing of pre- and post-billing events)
+but can be useful when configuring events.
 
 =cut
 
-sub initialize {
+sub targets {
   my $self = shift;
   my $time = time; # $opt{'time'}?
 
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
-
   my $eventpart = $self->eventpart;
   $eventpart =~ /^\d+$/ or die "bad eventpart $eventpart";
   my $eventtable = $self->eventtable;
@@ -286,38 +278,70 @@ sub initialize {
   my $linkage = '';
   # this is the 'object' side of the FROM clause
   if ( $eventtable ne 'cust_main' ) {
-    $linkage = ($self->eventtables_cust_join->{$eventtable} || '') . 
-        ' LEFT JOIN cust_main USING (custnum) '
+    $linkage = 
+        ($self->eventtables_cust_join->{$eventtable} || '') .
+        ' LEFT JOIN cust_main USING (custnum) ';
   }
 
   # this is the 'event' side
-  my $join  = FS::part_event_condition->join_conditions_sql( $eventtable );
+  my $join = FS::part_event_condition->join_conditions_sql( $eventtable );
   my $where = FS::part_event_condition->where_conditions_sql( $eventtable,
     'time' => $time
   );
-  $join = $linkage . 
+  $join = $linkage .
       " INNER JOIN part_event ON ( part_event.eventpart = $eventpart ) $join";
 
   $where .= ' AND cust_main.agentnum = '.$self->agentnum
     if $self->agentnum;
-  # don't enforce check_freq since this is a special, out-of-order check,
-  # and don't enforce disabled because we want to do this with the part_event 
-  # disabled.
+  # don't enforce check_freq since this is a special, out-of-order check
+  # and don't enforce disabled because we want to be able to see targets 
+  # for a disabled event
+
   my @objects = qsearch({
       table     => $eventtable,
       hashref   => {},
       addl_from => $join,
       extra_sql => "WHERE $where",
-      debug     => 1,
   });
-  warn "initialize: ".(scalar @objects) ." $eventtable objects found\n" 
-    if $DEBUG;
-  my $error = '';
+  my @tested_objects;
   foreach my $object ( @objects ) {
-    # test conditions
     my $cust_event = $self->new_cust_event($object, 'time' => $time);
     next unless $cust_event->test_conditions;
 
+    $object->set('cust_event', $cust_event);
+    push @tested_objects, $object;
+  }
+  @tested_objects;
+}
+
+=item initialize PARAMS
+
+Identify all objects eligible for this event and create L<FS::cust_event>
+records for each of them, as of the present time, with status "initial".  When 
+combined with conditions that prevent an event from running more than once
+(at all or within some period), this will exclude any objects that met the 
+conditions before the event was created.
+
+If an L<FS::part_event> object needs to be initialized, it should be created 
+in a disabled state to avoid running the event prematurely for any existing 
+objects.  C<initialize> will enable it once all the cust_event records 
+have been created.
+
+This may take some time, so it should be run from the job queue.
+
+=cut
+
+sub initialize {
+  my $self = shift;
+  my $error;
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my @objects = $self->targets;
+  foreach my $object ( @objects ) {
+    my $cust_event = $object->get('cust_event');
     $cust_event->status('initial');
     $error = $cust_event->insert;
     last if $error;
index f68f06b..6be2860 100644 (file)
                                           'Action',
                                         ],
                 'fields'             => [ 'eventpart',
-                                          'event',
+                                          $event_sub,
                                           $eventtable_sub,
                                           $check_freq_sub,
                                           $conditions_sub,
                                           $action_sub,
                                         ],
                 'links'              => [ $link,
-                                          $link,
+                                          '',
                                           '',
                                           '',
                                           '',
 %>
 <%once>
 
+my $link = [ $p.'edit/part_event.html?', 'eventpart' ];
+
+my $event_sub = sub {
+  my $part_event = shift;
+  my $onclick = include('/elements/popup_link_onclick.html',
+    action      => $p.'view/part_event-targets.html?'.$part_event->eventpart,
+    actionlabel => 'Event query - '.$part_event->event,
+    width       => 650,
+    height      => 420,
+    close_text  => 'Close',
+  );
+  [#rows
+    [#subcolumns
+      {
+        'data' => $part_event->event,
+        'link' => $p.'edit/part_event.html?'.$part_event->eventpart,
+      },
+      {
+        'data' => ' (query) ',
+        'size' => '-1',
+        'data_style'  => 'b',
+        'onclick' => $onclick,
+      },
+    ],
+  ];
+};
+
 my $eventtable_labels = FS::part_event->eventtable_labels;
 my $eventtable_sub = sub { $eventtable_labels->{ shift->eventtable }; };
 
@@ -131,8 +158,6 @@ my $action_sub = sub {
 
 };
 
-my $link = [ $p.'edit/part_event.html?', 'eventpart' ];
-
 </%once>
 <%init>
 
index d1f4b2f..af0c8fc 100644 (file)
 %                                       ? '<A HREF="'. $e->{'link'}. '">'
 %                                       : ''
 %                                   ).
+%                                   ( $e->{'onclick'} # don't use with 'link'
+%                                       ? '<A HREF="#" onclick="' .
+%                                         $e->{'onclick'}.'">'
+%                                       : ''
+%                                   ).
 %                                   ( $e->{'size'}
 %                                      ? '<FONT SIZE="'.uc($e->{'size'}).'">'
 %                                      : ''
 %                                       : ''
 %                                   ).
 %                                   ( $e->{'size'} ? '</FONT>' : '' ).
-%                                   ( $e->{'link'} ? '</A>'    : '' ).
+%                                   ( $e->{'link'} || $e->{'onclick'} 
+%                                       ? '</A>'
+%                                       : '' ).
 %                                   '</td>';
 %
 %                                 } @$rowref ).
diff --git a/httemplate/view/cust_pkg.cgi b/httemplate/view/cust_pkg.cgi
new file mode 100755 (executable)
index 0000000..d8a0041
--- /dev/null
@@ -0,0 +1,11 @@
+<% $cgi->redirect($path) %>
+<%init>
+# since cust_pkgs can't be viewed directly, just throw a redirect
+my ($pkgnum) = $cgi->keywords;
+$pkgnum =~ /^\d+$/ or die "invalid pkgnum '$pkgnum'";
+my $show = $FS::CurrentUser::CurrentUser->default_customer_view =~ /^(jumbo|packages)$/ ? '' : ';show=packages';
+
+my $self = FS::cust_pkg->by_key($pkgnum) or die "pkgnum $pkgnum not found";
+my $frag = 'cust_pkg'. $self->pkgnum;
+my $path = $p.'view/cust_main.cgi?custnum='.$self->custnum.";$show#$frag";
+</%init>
diff --git a/httemplate/view/part_event-targets.html b/httemplate/view/part_event-targets.html
new file mode 100644 (file)
index 0000000..c5faccf
--- /dev/null
@@ -0,0 +1,128 @@
+<& /elements/header-popup.html,
+     {
+       'title'   => 'Event query - '.$part_event->event,
+     }
+&>
+% if ( $objects > 0 ) {
+  <% emt("[quant,_1,$label]", $objects) %>
+%   if ( $part_event->eventtable ne 'cust_main' ) {
+  <% emt("belonging to [quant,_1,customer]", $customers) %>
+%   }
+<BR><BR>
+<TABLE class="grid" cellspacing=0 width="100%" style="font-size:80%">
+  <TR style='background-color:#cccccc'>
+%   foreach my $header ('Trigger', @cust_header, @header) {
+    <TH><% $header %></TH>
+%   }
+  </TR>
+
+%   my @rowcolors = ('ffffff','eeeeee');
+%   my $row = 0;
+  <TR style="background-color:#<% $rowcolors[$row++ % 2] %>">
+%   foreach my $object (@targets) {
+%     # now works for all eventtables, including cust_pkg
+%     my $link = $p . 'view/' . $part_event->eventtable . '.cgi?' .
+%        $object->$pkey;
+    <TD><A target="_blank" href="<% $link %>">
+    <% ucfirst $label %> #<% $object->$pkey %></A></TD>
+
+%     my $cust_main = $object->cust_main; # via Mixin
+%     my $i = 0; # hack to avoid messing with cust_aligns/colors/styles
+%     foreach (@cust_fields) {
+%       if ($cust_header[$i] eq 'Cust. Status') {
+    <TD style="text-align:center;
+               font-weight:bold;
+               color:#<% $cust_main->cust_statuscolor %>"><% $_->($cust_main) %></TD>
+%       }
+%       else {
+    <TD><% $_->($cust_main) %></TD>
+%       }
+%     $i++;
+%     } #foreach @cust_fields
+
+%     foreach (@fields) {
+    <TD><% ref($_) eq 'CODE' ? $_->($object) : $object->$_ %></TD>
+%     }
+  </TR>
+%   } #foreach $object
+
+</TABLE>
+
+% } #object > 0
+% else {
+
+<% emt("No matching ${label}s found.") %>
+
+%}
+<& /elements/footer.html &>
+<%once>
+use List::MoreUtils qw(uniq);
+</%once>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+  unless $curuser->access_right('Edit billing events')
+        || $curuser->access_right('Edit global billing events');
+
+my ($eventpart) = $cgi->keywords;
+$eventpart =~ /^\d+$/ or die 'illegal eventpart';
+
+my $part_event = FS::part_event->by_key($eventpart)
+  or die "Event definition $eventpart not found.\n";
+my @targets = $part_event->targets;
+my $total = @targets;
+
+# in imitation of search/elements/search-html.html
+my @header;
+my @fields;
+my ($pkey, $label);
+$pkey = dbdef->table($part_event->eventtable)->primary_key;
+
+for ($part_event->eventtable) {
+  if (/^cust_main$/) {
+    # very likely to appear in events
+    my %paybys = FS::payby->cust_payby2longname;
+    push @header, 'Balance', 'Payment Method';
+    push @fields, 'balance', sub{ $paybys{$_[0]->payby} };
+    $label = 'customer';
+  }
+  elsif (/^cust_bill$/) {
+    push @header, 'Invoice Date', 'Amount', 'Balance';
+    push @fields, date_format('_date'), 'charged', 'owed';
+    $label = 'invoice';
+  }
+  elsif (/^cust_statement$/) {
+    push @header, 'Statement Date', 'Amount', 'Balance';
+    push @fields, date_format('_date'), 'charged', 'owed';
+    $label = 'statement';
+  }
+  elsif (/^cust_pkg$/) {
+    push @header, 'Package', 'Next Bill', 'Frequency';
+    push @fields, sub {$_[0]->part_pkg->pkg}, date_format('bill'), 
+        sub {$_[0]->part_pkg->freq_pretty};
+    $label = 'package';
+  }
+  elsif (/^svc_acct$/) {
+    push @header, 'Username', 'Domain';
+    push @fields, 'username', 'domain';
+    $label = 'service';
+  }
+  else {}
+}
+
+my @cust_header = FS::UI::Web::cust_header();
+my @cust_fields = FS::UI::Web::cust_fields_subs();
+
+my $objects = scalar(@targets);
+my $customers = uniq(map {$_->cust_main->custnum} @targets);
+
+sub date_format {
+  my $column = shift;
+  sub { my $obj = shift;
+        my $value = $obj->get($column);
+        $value ? time2str('%b %d %Y', $value) : '';
+      };
+}
+
+</%init>