Skip to content
Browse files

Initial version of billing extension

  • Loading branch information...
1 parent 0d2e53e commit e16a864de9da3136cc16137499eb371ecb221777 @tla tla committed Aug 7, 2007
View
17 MANIFEST
@@ -0,0 +1,17 @@
+html/Callbacks/ActivityReportsBilling/Reports/Activity/Elements/LimitReport/AddOptions
+html/Callbacks/ActivityReportsBilling/Reports/Activity/Elements/LimitReport/AddReports
+html/Callbacks/ActivityReportsBilling/Reports/Activity/index.html/FlipInvoiced
+html/Reports/Activity/Elements/BillableActivityDetail
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install/RTx/Factory.pm
+inc/Module/Install/RTx.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+inc/Module/Install.pm
+lib/RT/Extension/ActivityReports/Billing.pm
+Makefile.PL
+MANIFEST
View
13 Makefile.PL
@@ -0,0 +1,13 @@
+use inc::Module::Install;
+
+RTx('RT-Extension-ActivityReports-Billing');
+license('perl');
+author('Tara Andrews <tla@bestpractical.com>');
+
+# XXX: This should be reported by M::I::RTx
+my ($lib_path) = $INC{'RT.pm'} =~ /^(.*)[\\\/]/;
+my $local_lib_path = "$RT::LocalPath/lib";
+unshift @INC, $local_lib_path, $lib_path;
+requires('RT::Extension::ActivityReports' => '0.10');
+
+&WriteAll;
View
33 README
@@ -0,0 +1,33 @@
+This extension is Copyright (C) 2007 Best Practical Solutions, LLC.
+
+It is freely redistributable under the terms of version 2 of the GNU GPL.
+
+SUMMARY ---
+
+RT::Extension::ActivityReports::Billing is a plugin to the module
+RT::Extension::ActivityReports that provides functionality for reporting on
+billable time, and an interface to record whether a billable transaction has
+been invoiced.
+
+PREREQUISITES ---
+
+This extension requires version 0.10 of RT::Extension::ActivityReports.
+
+INSTALLATION ---
+
+To install the extension:
+
+$ perl Makefile.PL
+
+(it may prompt you for the path to your RT.pm, if it can't
+automatically detect it.)
+
+$ make
+$ make install
+$ make initdb
+
+USAGE ---
+
+Once installed, the billing activity report can be accessed in the pick-list shown
+at http://<path_to_your_RT>/Reports/Activity/index.html
+
View
9 etc/initialdata
@@ -0,0 +1,9 @@
+@CustomFields = (
+ { Name => 'Invoiced', Type => 'SelectSingle',
+ LookupType => 'RT::Queue-RT::Ticket-RT::Transaction',
+ Values => [
+ { Name => 'No', SortOrder => 10 },
+ { Name => 'Yes', SortOrder => 20 },
+ ],
+ },
+);
View
5 html/Callbacks/ActivityReportsBilling/Reports/Activity/Elements/LimitReport/AddOptions
@@ -0,0 +1,5 @@
+<br />
+Include invoiced activity on billing report: <input type="checkbox" name="IncludeInvoiced" <% $IncludeInvoiced ? 'checked' : '' %>>
+<%args>
+$IncludeInvoiced => undef
+</%args>
View
4 html/Callbacks/ActivityReportsBilling/Reports/Activity/Elements/LimitReport/AddReports
@@ -0,0 +1,4 @@
+<option value="BillableActivityDetail" <% $ReportType =~ /BillableActivityDetail/ ? 'selected' : '' %>>Billable activity detail</option>
+<%args>
+$ReportType => ''
+</%args>
View
35 html/Callbacks/ActivityReportsBilling/Reports/Activity/index.html/FlipInvoiced
@@ -0,0 +1,35 @@
+<%init>
+use RT::Transaction;
+# Update the billing "invoiced" property for each relevant transaction, if we
+# have been asked to.
+foreach my $key( keys %ARGS ) {
+ if( $key =~ /^flipInvoiced_(\d+)$/ ) {
+ my $txn_id = $1;
+ flipinvoiced( $txn_id, $ARGS{$key} );
+ delete $ARGS{$key};
+ }
+}
+
+
+sub flipinvoiced {
+ # Given a transaction ID, change the state of its "Invoiced" custom field.
+ # That is, values change as follows: "Yes" -> "No"; "No" -> "Yes"; unset -> "Yes".
+
+ my( $txnid ) = @_;
+ my $txn = RT::Transaction->new( $session{'CurrentUser'} );
+ $txn->Load( $txnid );
+ unless( $txn->Id ) {
+ $RT::Logger->warning( "No transaction loaded for id $txnid" );
+ return;
+ }
+
+ if( $txn->CustomFieldValues( 'Invoiced' ) ) {
+ my $current = $txn->FirstCustomFieldValue( 'Invoiced' ) || '';
+ my $newval = $current eq 'Yes' ? 'No' : 'Yes';
+ my( $ret, $msg ) = $txn->AddCustomFieldValue( Field => 'Invoiced', Value => $newval );
+ $RT::Logger->warning( $msg ) unless $ret;
+ } else {
+ $RT::Logger->warning( "Transaction custom field 'Invoiced' does not exist for transaction $txnid" );
+ }
+}
+</%init>
View
95 html/Reports/Activity/Elements/BillableActivityDetail
@@ -0,0 +1,95 @@
+<form action="index.html" method=GET>
+<!-- This form is used to update the "invoiced" property of the listed transactions.
+ At the same time, it will resubmit the current report request. -->
+<input type="hidden" name="Query" value="<% $query %>">
+<input type="hidden" name="Start" value="<% $start %>">
+<input type="hidden" name="End" value="<% $end %>">
+<input type="hidden" name="Actor" value="<% $actor %>">
+<input type="hidden" name="ReportType" value="BillableActivityDetail">
+<input type="hidden" name="IncludeInvoiced" value="<% $IncludeInvoiced %>">
+<table style="width: 100%">
+<tr class="titlerow">
+<th>Queue</th><th>Date</th><th>Time</th><th>Ticket #</th><th>User</th><th>Time Worked</th><th>Invoiced</th><th>Description</th>
+</tr>
+% for my $item (@items) {
+<tr>
+<td><% $item->{queue} %></td>
+<td><% $item->{date} %></td>
+<td><% $item->{time} %></td>
+<td><% $item->{id} %></td>
+<td><% $item->{actor} %></td>
+<td><% $item->{timeworked} %></td>
+<td><% loc( $item->{invoiced} ) %> &nbsp;&nbsp;&nbsp; <input type="checkbox" name="flipInvoiced_<% $item->{txnid} %>">(Change this)</td>
+<td><% $item->{notes} %></td>
+</tr>
+% }
+<tr><td></td><td></td><td></td><td></td><td></td><td></td><td><& /Elements/Submit, Label => loc( 'Update invoicing data' ) &></td><td></td></tr>
+</table>
+</form>
+
+<%args>
+$query => ''
+$start => "2005/01/01"
+$end => "2006/01/01"
+$actor => ''
+</%args>
+
+<%init>
+use RT::Extension::ActivityReports qw( RelevantTxns );
+
+# Get the IncludeInvoiced argument (see
+# html/Callbacks/Reports/Activity/Elements/LimitReport/AddOptions)
+# from the original request, as it will not have been passed directly.
+my $original_args = $m->request_args();
+my $IncludeInvoiced = undef;
+if( exists( $original_args->{'IncludeInvoiced'} ) ) {
+ $IncludeInvoiced = $original_args->{'IncludeInvoiced'};
+}
+
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL(join " AND ", map {"($_)"} grep {/\S/} ($query, "Updated >= '$start' AND Updated <= '$end'"));
+my @items;
+while (my $ticket = $tickets->Next) {
+ my $txns = RelevantTxns( $ticket, start => $start, end => $end, query => $query, timed => 1 );
+ while (my $txn = $txns->Next) {
+ # Do the "invoiced" CF lookup, and skip invoiced transactions if we
+ # are limiting to unbilled ones.
+ my $invoiced = isinvoiced( $txn );
+ next if( !$IncludeInvoiced && $invoiced eq 'Yes' );
+
+ # We have to filter for actor here, not in the query. Alas.
+ if( $actor ) {
+ next unless $txn->CreatorObj->Name eq $actor;
+ }
+ push @items, { queue => $txn->TicketObj->QueueObj->Name,
+ id => $txn->TicketObj->id,
+ txnid => $txn->id,
+ date => (split ' ', $txn->CreatedObj->ISO)[0],
+ time => (split ' ', $txn->CreatedObj->ISO)[1],
+ timeworked => $txn->TimeTaken,
+ invoiced => $invoiced,
+ actor => $txn->CreatorObj->Name,
+ notes => ($txn->Content ne 'This transaction appears to have no content' ? substr($txn->Content, 0, 60) : $txn->BriefDescription)
+ };
+ }
+}
+
+@items = sort {
+ $a->{queue} cmp $b->{'queue'}
+ || $a->{'status'} cmp $b->{'status'}
+ || $a->{'id'} <=> $b->{'id'}
+ || $a->{'actor'} cmp $b->{'actor'}
+ || $a->{'notes'} cmp $b->{'notes'}
+} @items;
+
+sub isinvoiced {
+ # This field, if it exists, will have a 'Yes' or 'No' value, or will
+ # be unset. Unset means "no". Return the string for display.
+ my( $txn ) = @_;
+ my $isinvoiced = $txn->CustomFieldValues( 'Invoiced' ) ?
+ $txn->FirstCustomFieldValue( 'Invoiced' ) : 'No';
+ $isinvoiced = 'No' unless $isinvoiced;
+ return $isinvoiced;
+}
+
+</%init>
View
3 lib/RT/Extension/ActivityReports/Billing.pm
@@ -0,0 +1,3 @@
+package RT::Extension::ActivityReports::Billing;
+
+our $VERSION = '0.1';

0 comments on commit e16a864

Please sign in to comment.
Something went wrong with that request. Please try again.