diff options
-rw-r--r-- | FS/FS/Conf.pm | 7 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 1 | ||||
-rw-r--r-- | FS/FS/cust_bill.pm | 11 | ||||
-rw-r--r-- | FS/FS/cust_bill_ApplicationCommon.pm | 11 | ||||
-rw-r--r-- | FS/FS/part_event/Condition/cust_bill_past_promised.pm | 48 | ||||
-rw-r--r-- | httemplate/misc/cust_bill-promised_date.html | 19 | ||||
-rw-r--r-- | httemplate/misc/process/cust_bill-promised_date.html | 19 | ||||
-rwxr-xr-x | httemplate/search/cust_bill.html | 21 | ||||
-rw-r--r-- | httemplate/search/report_cust_bill.html | 19 | ||||
-rwxr-xr-x | httemplate/view/cust_bill.cgi | 21 | ||||
-rw-r--r-- | httemplate/view/cust_main/payment_history.html | 2 | ||||
-rw-r--r-- | httemplate/view/cust_main/payment_history/invoice.html | 23 |
12 files changed, 193 insertions, 9 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 3987fac..fcdcd57 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -4681,6 +4681,13 @@ and customer address. Include units.', 'description' => 'An alternate ordering of fields for the New Customer and Edit Customer screens.', 'type' => 'checkbox', }, + + { + 'key' => 'cust_bill-enable_promised_date', + 'section' => 'UI', + 'description' => 'Enable display/editing of the "promised payment date" field on invoices.', + 'type' => 'checkbox', + }, { 'key' => 'available-locales', diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 51daf44..18df708 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -532,6 +532,7 @@ sub tables_hashref { 'closed', 'char', 'NULL', 1, '', '', #not yet used much 'statementnum', 'int', 'NULL', '', '', '', #invoice aggregate statements 'agent_invid', 'int', 'NULL', '', '', '', #(varchar?) importing legacy + 'promised_date', @date_type, '', '', ], 'primary_key' => 'invnum', 'unique' => [ [ 'custnum', 'agent_invid' ] ], #agentnum? huh diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index ef6dc7b..41ff6aa 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -144,6 +144,8 @@ Specific use cases =item agent_invid - legacy invoice number +=item promised_date - customer promised payment date, for collection + =back =head1 METHODS @@ -5622,6 +5624,15 @@ sub search_sql_where { } + #promised_date - also has an option to accept nulls + if ( $param->{promised_date} ) { + my($beginning, $ending, $null) = @{$param->{promised_date}}; + + push @search, "(( cust_bill.promised_date >= $beginning AND ". + "cust_bill.promised_date < $ending )" . + ($null ? ' OR cust_bill.promised_date IS NULL ) ' : ')'); + } + #agent virtualization my $curuser = $FS::CurrentUser::CurrentUser; if ( $curuser->username eq 'fs_queue' diff --git a/FS/FS/cust_bill_ApplicationCommon.pm b/FS/FS/cust_bill_ApplicationCommon.pm index afb90f4..cadb8a7 100644 --- a/FS/FS/cust_bill_ApplicationCommon.pm +++ b/FS/FS/cust_bill_ApplicationCommon.pm @@ -435,6 +435,17 @@ sub apply_to_lineitems { } + # unset promised payment date if there is one + my $cust_bill = $self->cust_bill; + if ( $cust_bill->promised_date and $cust_bill->owed <= 0 ) { + $cust_bill->set('promised_date', ''); + my $error = $cust_bill->replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + #everything should always be applied to line items in full now... sanity check $applied = sprintf('%.2f', $applied); unless ( $applied == $self->amount ) { diff --git a/FS/FS/part_event/Condition/cust_bill_past_promised.pm b/FS/FS/part_event/Condition/cust_bill_past_promised.pm new file mode 100644 index 0000000..e861cb4 --- /dev/null +++ b/FS/FS/part_event/Condition/cust_bill_past_promised.pm @@ -0,0 +1,48 @@ +package FS::part_event::Condition::cust_bill_past_promised; + +use strict; +use FS::cust_bill; +use Time::Local 'timelocal'; + +use base qw( FS::part_event::Condition ); + +sub description { + 'Promised payment date has passed', +} + +sub eventtable_hashref { + { 'cust_main' => 0, + 'cust_bill' => 1, + 'cust_pkg' => 0, + }; +} + +sub option_fields { + ( + 'delay' => { label => 'Delay additional days', + type => 'text', + value => '0', + }, + ); +} + +sub condition { + # always return true if there is no promised_date + my($self, $cust_bill, %opt) = @_; + + my $delay = $self->option('delay') || 0; + my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($opt{'time'}))[0..5]; + my $as_of = timelocal(0,0,0,$mday,$mon,$year) - $delay * 86400; + $as_of >= ($cust_bill->promised_date || 0); +} + +sub condition_sql { + my( $class, $table, %opt ) = @_; + return 'true' if $opt{'driver_name'} ne 'Pg'; + my $delay = $class->condition_sql_option_integer('delay', 'Pg'); + my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($opt{'time'}))[0..5]; + my $as_of = timelocal(0,0,0,$mday,$mon,$year) . " - ($delay * 86400)"; + "(cust_bill.promised_date IS NULL OR $as_of >= cust_bill.promised_date)"; +} + +1; diff --git a/httemplate/misc/cust_bill-promised_date.html b/httemplate/misc/cust_bill-promised_date.html new file mode 100644 index 0000000..7b7b960 --- /dev/null +++ b/httemplate/misc/cust_bill-promised_date.html @@ -0,0 +1,19 @@ +<& /elements/header-popup.html, 'Edit promised date' &> +<FORM method="POST" action="process/cust_bill-promised_date.html"> +<B><% emt('Invoice #[_1]', $invnum) %></B><BR> +<% ntable('cccccc',2) %> +<INPUT TYPE="hidden" NAME="invnum" VALUE="<%$invnum%>"> +<& /elements/tr-input-date-field.html, + 'promised_date', + $cust_bill->promised_date, + emt('Promised date'), +&> +</TABLE> +<INPUT TYPE="submit" NAME="submit" VALUE="<% emt('Set date') %>"> +</FORM> +<& /elements/footer.html &> +<%init> +my ($invnum) = $cgi->keywords; +$invnum =~ /^\d+$/ or die "Illegal invnum"; +my $cust_bill = qsearchs('cust_bill', { invnum => $invnum }); +</%init> diff --git a/httemplate/misc/process/cust_bill-promised_date.html b/httemplate/misc/process/cust_bill-promised_date.html new file mode 100644 index 0000000..298b130 --- /dev/null +++ b/httemplate/misc/process/cust_bill-promised_date.html @@ -0,0 +1,19 @@ +<SCRIPT TYPE="text/javascript">window.top.location.reload()</SCRIPT> +<%init> +# XXX ACL? + +$cgi->param('invnum') =~ /^(\d+)$/ + or die "Illegal invnum"; +my $invnum = $1; + +my $promised_date = ''; +if ( length($cgi->param('promised_date')) ) { + $promised_date = parse_datetime($cgi->param('promised_date')) + or die "Illegal promised_date"; +} + +my $cust_bill = qsearchs('cust_bill', { invnum => $invnum }); +$cust_bill->promised_date($promised_date); +my $error = $cust_bill->replace; +die $error if $error; # nothing fancy here +</%init> diff --git a/httemplate/search/cust_bill.html b/httemplate/search/cust_bill.html index 4117112..813f9b8 100755 --- a/httemplate/search/cust_bill.html +++ b/httemplate/search/cust_bill.html @@ -128,7 +128,26 @@ if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) { $search{'newest_percust'} = 1; $count_query = "SELECT COUNT(DISTINCT cust_bill.custnum), 'N/A', 'N/A'"; } - + + # promised date + my $start_of_day = timelocal(0, 0, 0, (localtime(time))[3,4,5]); + foreach ( $cgi->param('promised_date') ) { + # only if at least one box is checked + $search{promised_date} ||= [ $start_of_day, $start_of_day, 0 ]; + if ($_ eq 'past') { + # accept everything before today + $search{promised_date}[0] = 0; + } + elsif ( $_ eq 'future' ) { + # accept everything after today + $search{promised_date}[1] = 4294967295; + } + elsif ( $_ eq 'null' ) { + # accept nulls + $search{promised_date}[2] = 1; + } + } + my $payby_sql = ''; $payby_sql = ' AND (' . join(' OR ', map { "cust_main.payby = '$_'" } $cgi->param('payby') ) . diff --git a/httemplate/search/report_cust_bill.html b/httemplate/search/report_cust_bill.html index 0f0d91b..51618fb 100644 --- a/httemplate/search/report_cust_bill.html +++ b/httemplate/search/report_cust_bill.html @@ -37,7 +37,23 @@ &> % } +% if ( $conf->exists('cust_bill-enable_promised_date') ) { <TR> + + <TD ALIGN="right" STYLE="vertical-align:text-top"> + <% emt('Promised payment date:') %></TD> + <TD> + <INPUT TYPE="checkbox" NAME="promised_date" CHECKED VALUE="null"> + <% emt('None') %> <BR> + <INPUT TYPE="checkbox" NAME="promised_date" CHECKED VALUE="past"> + <% emt('In the past') %><BR> + <INPUT TYPE="checkbox" NAME="promised_date" CHECKED VALUE="future"> + <% emt('In the future') %><BR> + </TD> + </TR> +% } + +<TR> <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD> <TD><% mt('Show only open invoices') |h %></TD> </TR> @@ -49,6 +65,7 @@ </TR> % } + </TABLE> <BR> @@ -62,6 +79,8 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('List invoices'); +my $conf = new FS::Conf; + my $title = 'Invoice Report'; #false laziness w/report_cust_pkg.html my @title_arg = (); diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi index 2ce294e..a8b4ac1 100755 --- a/httemplate/view/cust_bill.cgi +++ b/httemplate/view/cust_bill.cgi @@ -58,6 +58,27 @@ % } +% if ( $conf->exists('cust_bill-enable_promised_date') ) { +% my $onclick = include('/elements/popup_link_onclick.html', +% 'action' => $p.'misc/cust_bill-promised_date.html?'.$invnum, +% 'actionlabel' => emt('Set promised payment date'), +% 'width' => 320, +% 'height' => 240, +% ); +% $onclick = '<A HREF="#" onclick="'.$onclick.'">'; +% if ( $cust_bill->promised_date ) { +% my $date_format = $conf->config('date_format') || '%b %o, %Y'; + <% mt('Payment promised by [_1]', + time2str($date_format, $cust_bill->promised_date) ) %> + ( <% $onclick %><% mt('change') |h %></A> ) + <BR><BR> +% } +% elsif ( $cust_bill->owed > 0 ) { + <% $onclick %><% mt('Set promised payment date' ) |h %></A> + <BR><BR> +% } +% } + % if ( $curuser->access_right('Resend invoices') ) { <A HREF="<% $p %>misc/send-invoice.cgi?method=print;<% $link %>"><% mt('Re-print this invoice') |h %></A> diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html index 467c3bc..63708e6 100644 --- a/httemplate/view/cust_main/payment_history.html +++ b/httemplate/view/cust_main/payment_history.html @@ -398,6 +398,8 @@ my %opt = ( ) ); +$opt{'date_format'} ||= '%m/%d/%Y'; + #legacy invoices foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) { push @history, { diff --git a/httemplate/view/cust_main/payment_history/invoice.html b/httemplate/view/cust_main/payment_history/invoice.html index d7ee004..3028f0f 100644 --- a/httemplate/view/cust_main/payment_history/invoice.html +++ b/httemplate/view/cust_main/payment_history/invoice.html @@ -1,4 +1,4 @@ -<% $link %><% $invoice %><% $link ? '</A>' : '' %><% $delete %><% $events %> +<% $link %><% $invoice %><% $link ? '</A>' : '' %><% $delete %><% $under %> <%init> my( $cust_bill, %opt ) = @_; @@ -8,10 +8,17 @@ my $conf = new FS::Conf; my $curuser = $FS::CurrentUser::CurrentUser; my $invoice = emt("Invoice #[_1] (Balance [_2])",$cust_bill->display_invnum,$cust_bill->owed); -$invoice = '<B><FONT SIZE="+1" COLOR="#FF0000">' . + +my $under = ''; +if ( $cust_bill->owed > 0 ) { + $invoice = '<B><FONT SIZE="+1" COLOR="#FF0000">' . emt("Open Invoice #[_1] (Balance [_2])",$cust_bill->display_invnum,$cust_bill->owed) . - '</FONT></B>' -if ( $cust_bill->owed > 0 ); + '</FONT></B>'; + if ( $cust_bill->promised_date ) { + $under .= '<BR>'. emt('Payment promised on [_1]', + time2str($opt{'date_format'}, $cust_bill->promised_date)); + } +} #if $cust_bill->owed my $invnum = $cust_bill->invnum; @@ -34,10 +41,10 @@ if ( $cust_bill->num_cust_event || $curuser->access_right('View customer billing events') ) ) { - $events = - qq!<BR><FONT SIZE="-1"><A HREF="${p}search/cust_event.html?invnum=$invnum!. - '">( '.emt('View invoice events').' )</A></FONT>'; + $under .= + qq!<BR><A HREF="${p}search/cust_event.html?invnum=$invnum">( !. + emt('View invoice events').' )</A>'; } -# +$under = '<FONT SIZE="-1">'.$under.'</FONT>' if length($under); </%init> |