patron attributes - staff search and display

If ExtendedPatronAttributes is ON, patron attributes
can be displayed and edited by staff members.

The patron attributes are displayed and edited
in a new section (step 4) of the patron details page.  Each
attribute is identified by its type (including type code
and type description) and value (included value description
if the attribute is controlled by an authorised value category).

Note: if, for a repeatable attribute type, the staff member
enters multiple copies of that type with the same value, duplicates
are removed when the patron record is saved.  Also, if the type
is repeatable, a JavaScript link allows the staff member to
create new attributes of that type.

Signed-off-by: Joshua Ferraro <jmf@liblime.com>
This commit is contained in:
Galen Charlton 2008-05-12 08:32:56 -05:00 committed by Joshua Ferraro
parent 477bbe4d15
commit 944063b247
4 changed files with 221 additions and 4 deletions

View file

@ -22,6 +22,32 @@ patron <!-- TMPL_VAR NAME="surname" -->, <!-- TMPL_VAR name="firstname" --><!--
document.form.city.value=RegExp.$2;
});
});
function clear_entry(node) {
var original = node.parentNode.parentNode;
$("input", original).attr('value', '');
$("select", original).attr('value', '');
}
function clone_entry(node) {
var original = node.parentNode.parentNode;
var clone = original.cloneNode(true);
var newId = 50 + parseInt(Math.random() * 100000);
$("input", clone).attr('id', function() {
return this.id.replace(/patron_attr_\d+/, 'patron_attr_' + newId);
});
$("input", clone).attr('name', function() {
return this.name.replace(/patron_attr_\d+/, 'patron_attr_' + newId);
});
$("select", clone).attr('id', function() {
return this.id.replace(/patron_attr_\d+/, 'patron_attr_' + newId);
});
$("select", clone).attr('name', function() {
return this.name.replace(/patron_attr_\d+/, 'patron_attr_' + newId);
});
original.parentNode.insertBefore(clone, original.nextSibling);
}
//]]>
</script>
</head>
@ -106,6 +132,10 @@ patron </strong><!-- /TMPL_IF --></div>
<!-- TMPL_IF NAME="ERROR_dateexpiry" -->
<li id="ERROR_dateexpiry">Date of expiration is invalid.</li>
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="ERROR_extended_unique_id_failed" -->
<li id="ERROR_extended_unique_id_failed">The attribute value
<!-- TMPL_VAR NAME="ERROR_extended_unique_id_failed" --> is already is use by another patron record.</li>
<!-- /TMPL_IF -->
</ul>
</div>
<!-- /TMPL_IF -->
@ -793,6 +823,63 @@ patron </strong><!-- /TMPL_IF --></div>
</fieldset>
<!-- /TMPL_UNLESS -->
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="step_4" --><!-- TMPL_IF NAME="ExtendedPatronAttributes" -->
<fieldset class="rows" id="memberentry_patron_attributes">
<input type="hidden" name="setting_extended_patron_attributes" value="1">
<legend>Additional attributes and identifiers</legend>
<!-- TMPL_IF NAME="no_patron_attribute_types" -->
No patron attribute types defined.
<!-- TMPL_ELSE -->
<table>
<tr>
<th>Type</th>
<th colspan="2">Value</th>
</tr>
<!-- TMPL_LOOP NAME='patron_attributes' -->
<tr>
<td><!-- TMPL_VAR NAME="code" --> (<!-- TMPL_VAR NAME="description" -->)
</td>
<td>
<input type="hidden" id="<!-- TMPL_VAR NAME="form_id" -->_code" name="<!-- TMPL_VAR NAME="form_id" -->_code"
value="<!-- TMPL_VAR NAME="code" -->" />
<!-- TMPL_IF NAME="use_dropdown" -->
<select id="<!-- TMPL_VAR NAME="form_id" -->" name="<!-- TMPL_VAR NAME="form_id" -->">
<option value="" />
<!-- TMPL_LOOP NAME="auth_val_loop" -->
<!-- TMPL_IF NAME="selected" -->
<option value="<!-- TMPL_VAR NAME="authorised_value" -->" selected="selected">
<!-- TMPL_VAR NAME="lib" -->
</option>
<!-- TMPL_ELSE -->
<option value="<!-- TMPL_VAR NAME="authorised_value" -->" >
<!-- TMPL_VAR NAME="lib" -->
</option>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</select>
<!-- TMPL_ELSE -->
<input type="text" maxlength="30" value="<!-- TMPL_VAR NAME="value" -->"
id="<!-- TMPL_VAR NAME="form_id" -->" name="<!-- TMPL_VAR NAME="form_id" -->" />
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="password_allowed" -->
(Password: <input type="password" maxlength="30" value="<!-- TMPL_VAR NAME="password" -->"
id="<!-- TMPL_VAR NAME="form_id" -->_password" name="<!-- TMPL_VAR NAME="form_id" -->_password" />)
<!-- /TMPL_IF -->
</td>
<td>
<a href="#" onclick="clear_entry(this); return false;">Clear</a>
<!-- TMPL_IF NAME="repeatable" -->
<a href="#" onclick="clone_entry(this); return false;">New</a>
<!-- /TMPL_IF -->
</td>
</tr>
<!-- /TMPL_LOOP -->
</table>
<!-- /TMPL_IF -->
</fieldset>
<!-- /TMPL_IF--><!-- /TMPL_IF -->
<fieldset class="action">
<input type="submit" name="save" onclick="return check_form_borrowers();" value="Save" />
<!-- TMPL_IF NAME="opadd" -->

View file

@ -227,6 +227,29 @@ if (nodename =="barcodes[]"){
<!-- End Upload Patron Image Section -->
<!-- TMPL_IF NAME="ExtendedPatronAttributes" -->
<div id="patron-extended-attributes" style="padding-top: 1em;">
<h3>Additional attributes and identifiers</h3>
<table>
<tr>
<th>Type</th>
<th>Value</th>
</tr>
<!-- TMPL_LOOP NAME="patron_attributes" -->
<tr>
<td><!-- TMPL_VAR NAME="code" --> (<!-- TMPL_VAR NAME="description" -->)</td>
<td><!-- TMPL_VAR NAME="value" -->
<!-- TMPL_IF NAME="value_description" -->
(<!-- TMPL_VAR NAME="value_description" -->)
<!-- /TMPL_IF -->
</td>
</tr>
<!-- /TMPL_LOOP -->
</table>
</div>
<div class="action"><a href="memberentry.pl?op=modify&amp;borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->&amp;step=4">Edit</a></div>
<!-- /TMPL_IF -->
</div>
<div class="yui-u">
<div id="patron-library-details">
@ -292,7 +315,7 @@ if (nodename =="barcodes[]"){
<li><span class="label">Phone: </span><!-- TMPL_VAR NAME="altcontactphone" --></li></ol></div>
</div>
<div class="action"><a href="memberentry.pl?op=modify&amp;borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->&amp;step=2">Edit</a></div>
</div>
</div>

View file

@ -29,6 +29,8 @@ use C4::Auth;
use C4::Context;
use C4::Output;
use C4::Members;
use C4::Members::Attributes;
use C4::Members::AttributeTypes;
use C4::Koha;
use C4::Dates qw/format_date format_date_in_iso/;
use C4::Input;
@ -180,6 +182,7 @@ if ( (defined $newdata{'userid'}) && ($newdata{'userid'} eq '')){
$debug and warn join "\t", map {"$_: $newdata{$_}"} qw(dateofbirth dateenrolled dateexpiry);
my $loginexist=0;
my $extended_patron_attributes = ();
if ($op eq 'save' || $op eq 'insert'){
if (checkcardnumber($newdata{cardnumber},$newdata{borrowernumber})){
push @errors, 'ERROR_cardnumber';
@ -207,6 +210,16 @@ if ($op eq 'save' || $op eq 'insert'){
push @errors, "ERROR_login_exist";
$loginexist=1;
}
if (C4::Context->preference('ExtendedPatronAttributes')) {
$extended_patron_attributes = parse_extended_patron_attributes($input);
foreach my $attr (@$extended_patron_attributes) {
unless (C4::Members::Attributes::CheckUniqueness($attr->{code}, $attr->{value}, $borrowernumber)) {
push @errors, "ERROR_extended_unique_id_failed";
$template->param(ERROR_extended_unique_id_failed => "$attr->{code}/$attr->{value}");
}
}
}
}
if ($op eq 'modify' || $op eq 'insert'){
@ -229,12 +242,18 @@ if ((!$nok) and ($op eq 'insert' or $op eq 'save')){
my @orgs=split(/\|/,$data{'organisations'});
add_member_orgs($borrowernumber,\@orgs);
}
if (C4::Context->preference('ExtendedPatronAttributes') and $input->param('setting_extended_patron_attributes')) {
C4::Members::Attributes::SetBorrowerAttributes($borrowernumber, $extended_patron_attributes);
}
} elsif ($op eq 'save'){
if ($NoUpdateLogin) {
delete $newdata{'password'};
delete $newdata{'userid'};
}
&ModMember(%newdata);
if (C4::Context->preference('ExtendedPatronAttributes') and $input->param('setting_extended_patron_attributes')) {
C4::Members::Attributes::SetBorrowerAttributes($borrowernumber, $extended_patron_attributes);
}
}
print scalar ($destination eq "circ") ?
$input->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$borrowernumber") :
@ -253,7 +272,7 @@ if ($nok){
%data=%newdata;
$template->param( updtype => ($op eq 'add' ?'I':'M')); # used to check for $op eq "insert"... but we just changed $op!
unless ($step){
$template->param( step_1 => 1,step_2 => 1,step_3 => 1);
$template->param( step_1 => 1,step_2 => 1,step_3 => 1, step_4 => 1);
}
}
if (C4::Context->preference("IndependantBranches")) {
@ -268,12 +287,12 @@ if (C4::Context->preference("IndependantBranches")) {
if ($op eq 'add'){
my $arg2 = $newdata{'dateenrolled'} || C4::Dates->today('iso');
$data{'dateexpiry'} = GetExpiryDate($newdata{'categorycode'},$arg2);
$template->param( updtype => 'I',step_1=>1,step_2=>1,step_3=>1);
$template->param( updtype => 'I',step_1=>1,step_2=>1,step_3=>1, step_4 => 1);
}
if ($op eq "modify") {
$template->param( updtype => 'M',modify => 1 );
$template->param( step_1=>1,step_2=>1,step_3=>1) unless $step;
$template->param( step_1=>1,step_2=>1,step_3=>1, step_4 => 1) unless $step;
}
# my $cardnumber=$data{'cardnumber'};
$data{'cardnumber'}=fixup_cardnumber($data{'cardnumber'}) if $op eq 'add';
@ -482,6 +501,11 @@ foreach (qw(dateenrolled dateexpiry dateofbirth)) {
$template->param( $_ => $data{$_});
}
if (C4::Context->preference('ExtendedPatronAttributes')) {
$template->param(ExtendedPatronAttributes => 1);
patron_attributes_form($template, $borrowernumber);
}
$template->param( "showguarantor" => ($category_type=~/A|I|S|X/) ? 0 : 1); # associate with step to know where you are
$debug and warn "memberentry step: $step";
$template->param(%data);
@ -522,6 +546,83 @@ $template->param(
output_html_with_http_headers $input, $cookie, $template->output;
sub parse_extended_patron_attributes {
my ($input) = @_;
my @patron_attr = grep { /^patron_attr_\d+$/ } $input->param();
my @attr = ();
my %dups = ();
foreach my $key (@patron_attr) {
my $value = $input->param($key);
next unless defined($value) and $value ne '';
my $password = $input->param("${key}_password");
my $code = $input->param("${key}_code");
next if exists $dups{$code}->{$value};
$dups{$code}->{$value} = 1;
push @attr, { code => $code, value => $value, password => $password };
}
return \@attr;
}
sub patron_attributes_form {
my $template = shift;
my $borrowernumber = shift;
my @types = C4::Members::AttributeTypes::GetAttributeTypes();
if (scalar(@types) == 0) {
$template->param(no_patron_attribute_types => 1);
return;
}
my $attributes = C4::Members::Attributes::GetBorrowerAttributes($borrowernumber);
# map patron's attributes into a more convenient structure
my %attr_hash = ();
foreach my $attr (@$attributes) {
push @{ $attr_hash{$attr->{code}} }, $attr;
}
my @attribute_loop = ();
my $i = 0;
foreach my $type_code (map { $_->{code} } @types) {
my $attr_type = C4::Members::AttributeTypes->fetch($type_code);
my $entry = {
code => $attr_type->code(),
description => $attr_type->description(),
repeatable => $attr_type->repeatable(),
password_allowed => $attr_type->password_allowed(),
category => $attr_type->authorised_value_category(),
password => '',
};
if (exists $attr_hash{$attr_type->code()}) {
foreach my $attr (@{ $attr_hash{$attr_type->code()} }) {
my $newentry = { map { $_ => $entry->{$_} } %$entry };
$newentry->{value} = $attr->{value};
$newentry->{password} = $attr->{password};
$newentry->{use_dropdown} = 0;
if ($attr_type->authorised_value_category()) {
$newentry->{use_dropdown} = 1;
$newentry->{auth_val_loop} = GetAuthorisedValues($attr_type->authorised_value_category(), $attr->{value});
}
$i++;
$newentry->{form_id} = "patron_attr_$i";
#use Data::Dumper; die Dumper($entry) if $entry->{use_dropdown};
push @attribute_loop, $newentry;
}
} else {
$i++;
my $newentry = { map { $_ => $entry->{$_} } %$entry };
if ($attr_type->authorised_value_category()) {
$newentry->{use_dropdown} = 1;
$newentry->{auth_val_loop} = GetAuthorisedValues($attr_type->authorised_value_category());
}
$newentry->{form_id} = "patron_attr_$i";
push @attribute_loop, $newentry;
}
}
$template->param(patron_attributes => \@attribute_loop);
}
# Local Variables:
# tab-width: 8
# End:

View file

@ -37,6 +37,7 @@ use C4::Context;
use C4::Auth;
use C4::Output;
use C4::Members;
use C4::Members::Attributes;
use C4::Dates;
use C4::Reserves;
use C4::Circulation;
@ -335,6 +336,11 @@ my $branch=C4::Context->userenv->{'branch'};
$template->param($data);
if (C4::Context->preference('ExtendedPatronAttributes')) {
$template->param(ExtendedPatronAttributes => 1);
$template->param(patron_attributes => C4::Members::Attributes::GetBorrowerAttributes($borrowernumber));
}
$template->param(
detailview => 1,
DHTMLcalendar_dateformat=>C4::Dates->DHTMLcalendar(),