diff options
| -rw-r--r-- | FS/FS.pm | 2 | ||||
| -rw-r--r-- | FS/FS/Conf.pm | 12 | ||||
| -rw-r--r-- | FS/FS/Mason.pm | 1 | ||||
| -rw-r--r-- | FS/FS/Schema.pm | 12 | ||||
| -rw-r--r-- | FS/FS/cust_main.pm | 7 | ||||
| -rw-r--r-- | FS/FS/cust_main_note.pm | 34 | ||||
| -rw-r--r-- | FS/FS/cust_note_class.pm | 105 | ||||
| -rw-r--r-- | FS/MANIFEST | 2 | ||||
| -rw-r--r-- | FS/t/cust_note_class.t | 6 | ||||
| -rw-r--r-- | httemplate/browse/cust_note_class.html | 35 | ||||
| -rwxr-xr-x | httemplate/edit/cust_main_note.cgi | 19 | ||||
| -rw-r--r-- | httemplate/edit/cust_note_class.html | 7 | ||||
| -rw-r--r-- | httemplate/edit/elements/class_Common.html | 6 | ||||
| -rwxr-xr-x | httemplate/edit/process/cust_main_note.cgi | 4 | ||||
| -rw-r--r-- | httemplate/edit/process/cust_note_class.html | 12 | ||||
| -rw-r--r-- | httemplate/elements/menu.html | 3 | ||||
| -rwxr-xr-x | httemplate/view/cust_main.cgi | 2 | ||||
| -rwxr-xr-x | httemplate/view/cust_main/notes.html | 102 | 
18 files changed, 349 insertions, 22 deletions
@@ -292,6 +292,8 @@ L<FS::cust_main_exemption> - Customer tax exemption class  L<FS::cust_main_note> - Customer note class +L<FS::cust_note_class> - Customer note classification class +  L<FS::banned_pay> - Banned payment information class  L<FS::cust_bill> - Invoice class diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 95a9574a2..116533bd2 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -4236,6 +4236,18 @@ and customer address. Include units.',      'select_enum' => [ 'Classic', 'Recurring' ],    }, +  { +    'key'         => 'note-classes', +    'section'     => 'UI', +    'description' => 'Use customer note classes', +    'type'        => 'select', +    'select_hash' => [ +                       0 => 'Disabled', +		       1 => 'Enabled', +		       2 => 'Enabled, with tabs', +		     ], +  }, +    { key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },    { key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },    { key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" }, diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index d3c04db27..b66dc8cee 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -258,6 +258,7 @@ if ( -e $addl_handler_use_file ) {    use FS::acct_snarf;    use FS::part_pkg_discount;    use FS::svc_cert; +  use FS::cust_note_class;    # Sammath Naur    if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index ce1cd4fae..a7f0bfacc 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -930,6 +930,7 @@ sub tables_hashref {        'columns' => [          'notenum',  'serial',  '',     '', '', '',           'custnum',  'int',  '',     '', '', '',  +        'classnum',      'int',     'NULL', '', '', '',           '_date',    @date_type, '', '',           'otaker',   'varchar', 'NULL',    32, '', '',           'usernum',   'int', 'NULL', '', '', '', @@ -940,6 +941,17 @@ sub tables_hashref {        'index' => [ [ 'custnum' ], [ '_date' ], [ 'usernum' ], ],      }, +    'cust_note_class' => { +      'columns' => [ +        'classnum',    'serial',   '',      '', '', '',  +        'classname',   'varchar',  '', $char_d, '', '',  +        'disabled',    'char', 'NULL',       1, '', '',  +      ], +      'primary_key' => 'classnum', +      'unique' => [], +      'index' => [ ['disabled'] ], +    }, +      'cust_category' => {        'columns' => [          'categorynum',   'serial',  '', '', '', '',  diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index e4ff3bf3c..cf4c6d915 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2211,12 +2211,13 @@ Returns all notes (see L<FS::cust_main_note>) for this customer.  =cut  sub notes { -  my $self = shift; -  #order by? +   my($self,$orderby_classnum) = (shift,shift); +   my $orderby = "_DATE DESC"; +   $orderby = "CLASSNUM ASC, $orderby" if $orderby_classnum;    qsearch( 'cust_main_note',             { 'custnum' => $self->custnum },  	   '', -	   'ORDER BY _DATE DESC' + 	   "ORDER BY $orderby",  	 );  } diff --git a/FS/FS/cust_main_note.pm b/FS/FS/cust_main_note.pm index 0a203a8f4..06da0965a 100644 --- a/FS/FS/cust_main_note.pm +++ b/FS/FS/cust_main_note.pm @@ -4,6 +4,7 @@ use strict;  use base qw( FS::otaker_Mixin FS::Record );  use Carp;  use FS::Record qw( qsearch qsearchs ); +use FS::cust_note_class;  =head1 NAME @@ -38,6 +39,8 @@ primary key  =item custnum +=item classnum +  =item _date  =item usernum @@ -106,6 +109,7 @@ sub check {    my $error =       $self->ut_numbern('notenum')      || $self->ut_number('custnum') +    || $self->ut_foreign_keyn('classnum', 'cust_note_class', 'classnum')      || $self->ut_numbern('_date')      || $self->ut_textn('otaker')      || $self->ut_anything('comments') @@ -115,6 +119,36 @@ sub check {    $self->SUPER::check;  } +=item cust_note_class + +Returns the customer note class, as an FS::cust_note_class object, or the empty +string if there is no note class. + +=cut + +sub cust_note_class { +  my $self = shift; +  if ( $self->classnum ) { +    qsearchs('cust_note_class', { 'classnum' => $self->classnum } ); +  } else { +    return ''; +  }  +} + +=item classname  + +Returns the customer note class name, or the empty string if there is no  +customer note class. + +=cut + +sub classname { +  my $self = shift; +  my $cust_note_class = $self->cust_note_class; +  $cust_note_class ? $cust_note_class->classname : ''; +} + +  #false laziness w/otaker_Mixin & cust_attachment  sub otaker {    my $self = shift; diff --git a/FS/FS/cust_note_class.pm b/FS/FS/cust_note_class.pm new file mode 100644 index 000000000..0cb967754 --- /dev/null +++ b/FS/FS/cust_note_class.pm @@ -0,0 +1,105 @@ +package FS::cust_note_class; + +use strict; +use base qw( FS::class_Common ); +use FS::cust_main_note; + +=head1 NAME + +FS::cust_note_class - Object methods for cust_note_class records + +=head1 SYNOPSIS + +  use FS::cust_note_class; + +  $record = new FS::cust_note_class \%hash; +  $record = new FS::cust_note_class { 'column' => 'value' }; + +  $error = $record->insert; + +  $error = $new_record->replace($old_record); + +  $error = $record->delete; + +  $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_note_class object represents a customer note class. Every customer +note (see L<FS::cust_main_note) has, optionally, a note class. This class  +inherits from FS::class_Common.  The following fields are currently supported: + +=over 4 + +=item classnum + +primary key + +=item classname + +classname + +=item disabled + +disabled + + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new customer note class.  To add the note class to the database, +see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to.  You can ask the object for a copy with the I<hash> method. + +=cut + +sub table { 'cust_note_class'; } +sub _target_table { 'cust_main_note'; } + +=item insert + +Adds this record to the database.  If there is an error, returns the error, +otherwise returns false. + +=cut + +=item delete + +Delete this record from the database. + +=cut + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database.  If there is an error, +returns the error, otherwise returns false. + +=cut + +=item check + +Checks all fields to make sure this is a valid note class.  If there is +an error, returns the error, otherwise returns false.  Called by the insert +and replace methods. + +=cut + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L<FS::cust_main_note>, L<FS::Record>, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/MANIFEST b/FS/MANIFEST index 8b9cec91d..58728e4b0 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -541,3 +541,5 @@ t/part_pkg_discount.t  FS/svc_cert.pm  t/svc_cert.t  FS/cust_main/Status.pm +FS/cust_note_class.pm +t/cust_note_class.t diff --git a/FS/t/cust_note_class.t b/FS/t/cust_note_class.t new file mode 100644 index 000000000..feeb78c28 --- /dev/null +++ b/FS/t/cust_note_class.t @@ -0,0 +1,6 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::cust_note_class; +$loaded=1; +print "ok 1\n"; + diff --git a/httemplate/browse/cust_note_class.html b/httemplate/browse/cust_note_class.html new file mode 100644 index 000000000..889ee3208 --- /dev/null +++ b/httemplate/browse/cust_note_class.html @@ -0,0 +1,35 @@ +<% include( 'elements/browse.html', +                 'title'       => 'Customer note classes', +                 'html_init'   => $html_init, +                 'name'        => 'customer note classes', +                 'disableable' => 1, +                 'disabled_statuspos' => 2, +                 'query'       => { 'table'     => 'cust_note_class', +                                    'hashref'   => {}, +                                    'order_by' => 'ORDER BY classnum', +                                  }, +                 'count_query' => $count_query, +                 'header'      => $header, +                 'fields'      => $fields, +                 'links'       => $links, +             ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $html_init =  +  'Customer note classes define groups of notes for reporting.<BR><BR>'. +  qq!<A HREF="${p}edit/cust_note_class.html"><I>Add a customer note class</I></A><BR><BR>!; + +my $count_query = 'SELECT COUNT(*) FROM cust_note_class'; + +my $link = [ $p.'edit/cust_note_class.html?', 'classnum' ]; + +my $header = [ '#', 'Class' ]; +my $fields = [ 'classnum', 'classname' ]; +my $links  = [ $link, $link ]; + +</%init> + diff --git a/httemplate/edit/cust_main_note.cgi b/httemplate/edit/cust_main_note.cgi index 439c84414..c4ec071b8 100755 --- a/httemplate/edit/cust_main_note.cgi +++ b/httemplate/edit/cust_main_note.cgi @@ -6,6 +6,18 @@  <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">  <INPUT TYPE="hidden" NAME="notenum" VALUE="<% $notenum %>"> +% if ($conf->exists('note-classes') && $conf->config('note-classes') > 0) { +    Class   +	<% include( '/elements/select-table.html', +                 'table'       => 'cust_note_class', +                 'name_col'    => 'classname', +                 'curr_value'  => $classnum, +                 'empty_label' => '(none)', +                 'hashref'     => { 'disabled' => '' }, +         ) %> +    <BR> +% } +  % if( $FS::CurrentUser::CurrentUser->option('disable_html_editor') ) {    <TEXTAREA NAME="comment_plain" ROWS="12" COLS="60"><%     join '', split /<br \/>| /, $comment  @@ -25,21 +37,26 @@  <%init> +my $conf = new FS::Conf; +  my $comment;  my $notenum = ''; +my $classnum;  if ( $cgi->param('error') ) {    $comment     = $cgi->param('comment'); +  $classnum = $cgi->param('classnum');  } elsif ( $cgi->param('notenum') =~ /^(\d+)$/ ) {    $notenum = $1;    die "illegal query ". $cgi->keywords unless $notenum;    my $note = qsearchs('cust_main_note', { 'notenum' => $notenum });    die "no such note: ". $notenum unless $note;    $comment = $note->comments; +  $classnum = $note->classnum;  }  $comment =~ s/\r//g; # remove weird line breaks to protect FCKeditor -$cgi->param('custnum') =~ /^(\d+)$/ or die "illeagl custnum"; +$cgi->param('custnum') =~ /^(\d+)$/ or die "illegal custnum";  my $custnum = $1;  my $action = $notenum ? 'Edit' : 'Add'; diff --git a/httemplate/edit/cust_note_class.html b/httemplate/edit/cust_note_class.html new file mode 100644 index 000000000..038d376af --- /dev/null +++ b/httemplate/edit/cust_note_class.html @@ -0,0 +1,7 @@ +<% include( 'elements/class_Common.html', +              'name'   => 'Customer Note Class', +              'table'  => 'cust_note_class', +          'nocat' => 1, +          ) +%> + diff --git a/httemplate/edit/elements/class_Common.html b/httemplate/edit/elements/class_Common.html index b5f493991..2daf0e77f 100644 --- a/httemplate/edit/elements/class_Common.html +++ b/httemplate/edit/elements/class_Common.html @@ -26,7 +26,11 @@ die "access denied"  my %opt = @_;  my $table = $opt{'table'}; + +my @category; +unless ( $opt{'nocat'} ) {  ( my $category_table = $table ) =~ s/class/category/ or die; +    @category = qsearch($category_table, { 'disabled' => '' }); +} -my @category = qsearch($category_table, { 'disabled' => '' });  </%init> diff --git a/httemplate/edit/process/cust_main_note.cgi b/httemplate/edit/process/cust_main_note.cgi index f904c5968..227297eef 100755 --- a/httemplate/edit/process/cust_main_note.cgi +++ b/httemplate/edit/process/cust_main_note.cgi @@ -18,6 +18,9 @@ $cgi->param('notenum') =~ /^(\d*)$/    or die "Illegal notenum: ". $cgi->param('notenum');  my $notenum = $1; +$cgi->param('classnum') =~ /^(\d*)$/; +my $classnum = $1; +  my $comment = $cgi->param('comment_html') ||                 join("<br />\n",                   split "(?:\r|\n)+", $cgi->param('comment_plain') @@ -26,6 +29,7 @@ my $comment = $cgi->param('comment_html') ||  my $new = new FS::cust_main_note ( {    notenum  => $notenum,    custnum  => $custnum, +  classnum => $classnum ? $classnum : undef,    _date    => time,    usernum  => $FS::CurrentUser::CurrentUser->usernum,    comments => $comment, diff --git a/httemplate/edit/process/cust_note_class.html b/httemplate/edit/process/cust_note_class.html new file mode 100644 index 000000000..25e21652c --- /dev/null +++ b/httemplate/edit/process/cust_note_class.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', +               'table'       => 'cust_note_class', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> + diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index db8d9d532..a981abd87 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -434,6 +434,9 @@ tie my %config_cust, 'Tie::IxHash',    'Customer categories' =>  [ $fsurl.'browse/cust_category.html', 'Customer categories define groups of customer classes.' ],  ; +$config_cust{'Customer note classes'} = [ $fsurl.'browse/cust_note_class.html', 'Customer note classes define groups of notes for reporting.' ] +    if ($conf->exists('note-classes') && $conf->config('note-classes') > 0); +  tie my %config_agent, 'Tie::IxHash',    'Agent types' => [ $fsurl.'browse/agent_type.cgi', 'Agent types define groups of package definitions that you can then assign to particular agents' ],    'Agents'      => [ $fsurl.'browse/agent.cgi', 'Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their type)' ], diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index c4c0b5726..e6db0a6c1 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -160,7 +160,7 @@ Comments  <BR><BR>  % }  <A NAME="notes"> -% my $notecount = scalar($cust_main->notes()); +% my $notecount = scalar($cust_main->notes(0));  % if ( ! $conf->exists('cust_main-disable_notes') || $notecount) {  %   unless ( $view eq 'notes' && $cust_main->comments !~ /[^\s\n\r]/ ) { diff --git a/httemplate/view/cust_main/notes.html b/httemplate/view/cust_main/notes.html index 1283b19da..237838029 100755 --- a/httemplate/view/cust_main/notes.html +++ b/httemplate/view/cust_main/notes.html @@ -1,24 +1,43 @@  % if ( scalar(@notes) ) { -  <% include('/elements/init_overlib.html') %> - -  <% include("/elements/table-grid.html") %> +<SCRIPT TYPE="text/javascript"> + +    function display_notes_classnum(classnum){ +	document.getElementById('notes_'+classnum).style.display = 'block'; +	document.getElementById('notes_tablink_'+classnum).style.fontWeight = 'bold'; + +	var divs = document.getElementsByTagName("div"); +	var i; +	for(i=0; i < divs.length; i++){ +	    var d = divs[i]; +	    if(d.id.length > 6 && d.id.substring(0,6) == 'notes_') { +		if(divs[i].id != 'notes_'+classnum) { +		    divs[i].style.display = 'none'; +		} +	    } +	} +	 +	var as = document.getElementsByTagName("a"); +	for(i=0; i < as.length; i++){ +	    var a = as[i]; +	    if(a.id.length > 14 && a.id.substring(0,14) == 'notes_tablink_') { +		if(as[i].id != 'notes_tablink_'+classnum) { +		    as[i].style.fontWeight = 'normal'; +		} +	    } +	} +    } + +</SCRIPT> -  <TR> -    <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH> -%   if ( $conf->exists('cust_main_note-display_times') ) { -      <TH CLASS="grid" BGCOLOR="#cccccc">Time</TH> -%   } -    <TH CLASS="grid" BGCOLOR="#cccccc">Person</TH> -    <TH CLASS="grid" BGCOLOR="#cccccc">Note</TH> -%   if ($curuser->access_right('Edit customer note') ) { -    <TH CLASS="grid" BGCOLOR="#cccccc"> </TH> -%   } -  </TR> +  <% include('/elements/init_overlib.html') %>  % my $bgcolor1 = '#eeeeee';  % my $bgcolor2 = '#ffffff';  % my $bgcolor = ''; +% my $last_classnum = -1; +% my $skipheader = 0; +% my %classes = ();  %  % foreach my $note (@notes) {  % @@ -46,12 +65,50 @@  %   if ($curuser->access_right('Edit customer note') ) {  %     $edit = qq! <A HREF="javascript:void(0);" $clickjs>(edit)</A>!;  %   } +% +% if ( $last_classnum != $note->classnum && !$skipheader ) { +% my $tmp_classnum = $note->classnum ? $note->classnum : 0; +% $classes{$tmp_classnum} = $note->classname ne '' ? $note->classname  +%						     : 'Other'; +% if ( $last_classnum != -1 ) { +    </TABLE> +  </DIV> +% } +% my $display = ($tmp_classnum == 0 || !$conf->exists('note-classes')  +%				    || $conf->config('note-classes') < 2)  +%							    ? 'block' : 'none'; +	<DIV 	id="notes_<% $tmp_classnum %>" +		style="display:<% $display %>" +	> +	<% include("/elements/table-grid.html") %> +	<TR> +	    <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH> +%   if ( $conf->exists('cust_main_note-display_times') ) { +	    <TH CLASS="grid" BGCOLOR="#cccccc">Time</TH> +%   } +	    <TH CLASS="grid" BGCOLOR="#cccccc">Person</TH> +%   if ($conf->exists('note-classes') && $conf->config('note-classes') == 1) { +	    <TH CLASS="grid" BGCOLOR="#cccccc">Class</TH> +%   } +	    <TH CLASS="grid" BGCOLOR="#cccccc">Note</TH> +%   if ($curuser->access_right('Edit customer note') ) { +	    <TH CLASS="grid" BGCOLOR="#cccccc"> </TH> +%   } +	</TR> +% $skipheader = (!$conf->exists('note-classes') || $conf->config('note-classes') < 2); +% $last_classnum = $note->classnum; +% }      <TR>        <% note_datestr($note,$conf,$bgcolor) %>        <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">           <% $note->usernum ? $note->access_user->name : $note->otaker %>        </TD> +% if ($conf->exists('note-classes') && $conf->config('note-classes') == 1) { +      <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> +	<% $note->classname %>    +      </TD> +% }        <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">          <% $note->comments | defang %>        </TD> @@ -63,6 +120,19 @@  % } #end display notes  </TABLE> +</DIV> + +% if ( $conf->exists('note-classes') && $conf->config('note-classes') == 2 ) { +% 	my($classnum,$classname); +Show notes of class:    +% 	foreach my $classnum ( sort { $b <=> $a } (keys %classes) ) { +	    <A	id="notes_tablink_<% $classnum %>" +		HREF="javascript:display_notes_classnum(<% $classnum %>)" +		style="font-weight: <% $classnum == 0 ? 'bold' : 'normal' %>" +	    ><% $classes{$classnum} %></A> +% 	} +    <BR> +% }  % }  <%init> @@ -77,9 +147,9 @@ my(%opt) = @_;  my $custnum = $opt{'custnum'};  my $cust_main = qsearchs('cust_main', {'custnum' => $custnum} ); -die "Custimer not found!" unless $cust_main; +die "Customer not found!" unless $cust_main; -my (@notes) = $cust_main->notes(); +my (@notes) = $cust_main->notes($conf->exists('note-classes') && $conf->config('note-classes') == 2);  #subroutines  | 
