From: mark Date: Wed, 15 Feb 2012 01:32:10 +0000 (+0000) Subject: query billing events to see affected objects, #15142 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=c6e4f9460f44a7440ef2fa7e67ed51dfe40a7668 query billing events to see affected objects, #15142 --- diff --git a/FS/FS/part_event.pm b/FS/FS/part_event.pm index dfbb6a5d3..31d2afd23 100644 --- a/FS/FS/part_event.pm +++ b/FS/FS/part_event.pm @@ -253,31 +253,23 @@ sub templatename { } } -=item initialize PARAMS - -Identify all objects eligible for this event and create L -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 object needs to be initialized, it should be created -in a disabled state to avoid running the event prematurely for any existing -objects. C will enable it once all the cust_event records -have been created. +Returns all objects (of type C, for this object's +C) eligible for processing under this event, as of right now. +The L 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 +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 object needs to be initialized, it should be created +in a disabled state to avoid running the event prematurely for any existing +objects. C 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; diff --git a/httemplate/browse/part_event.html b/httemplate/browse/part_event.html index f68f06b9f..6be28602d 100644 --- a/httemplate/browse/part_event.html +++ b/httemplate/browse/part_event.html @@ -22,14 +22,14 @@ 'Action', ], 'fields' => [ 'eventpart', - 'event', + $event_sub, $eventtable_sub, $check_freq_sub, $conditions_sub, $action_sub, ], 'links' => [ $link, - $link, + '', '', '', '', @@ -40,6 +40,33 @@ %> <%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' ]; - <%init> diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html index d1f4b2f1e..af0c8fc09 100644 --- a/httemplate/search/elements/search-html.html +++ b/httemplate/search/elements/search-html.html @@ -296,6 +296,11 @@ % ? '' % : '' % ). +% ( $e->{'onclick'} # don't use with 'link' +% ? '' +% : '' +% ). % ( $e->{'size'} % ? '' % : '' @@ -310,7 +315,9 @@ % : '' % ). % ( $e->{'size'} ? '' : '' ). -% ( $e->{'link'} ? '' : '' ). +% ( $e->{'link'} || $e->{'onclick'} +% ? '' +% : '' ). % ''; % % } @$rowref ). diff --git a/httemplate/view/cust_pkg.cgi b/httemplate/view/cust_pkg.cgi new file mode 100755 index 000000000..d8a0041ee --- /dev/null +++ b/httemplate/view/cust_pkg.cgi @@ -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"; + diff --git a/httemplate/view/part_event-targets.html b/httemplate/view/part_event-targets.html new file mode 100644 index 000000000..c5faccfd6 --- /dev/null +++ b/httemplate/view/part_event-targets.html @@ -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) %> +% } +

+ + +% foreach my $header ('Trigger', @cust_header, @header) { + +% } + + +% my @rowcolors = ('ffffff','eeeeee'); +% my $row = 0; + +% foreach my $object (@targets) { +% # now works for all eventtables, including cust_pkg +% my $link = $p . 'view/' . $part_event->eventtable . '.cgi?' . +% $object->$pkey; + + +% 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') { + +% } +% else { + +% } +% $i++; +% } #foreach @cust_fields + +% foreach (@fields) { + +% } + +% } #foreach $object + +
<% $header %>
+ <% ucfirst $label %> #<% $object->$pkey %><% $_->($cust_main) %><% $_->($cust_main) %><% ref($_) eq 'CODE' ? $_->($object) : $object->$_ %>
+ +% } #object > 0 +% else { + +<% emt("No matching ${label}s found.") %> + +%} +<& /elements/footer.html &> +<%once> +use List::MoreUtils qw(uniq); + +<%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) : ''; + }; +} + +