Bug 21946: Display parent-child relationship on smart-rules.pl
To test: 1 - Set some itemtypes to have a parent 2 - Browse to Administration -> Circulation and fines rules 3 - Note new description of parent/child relationships at top of page 4 - Note that itemtype dropdown for circ rules shows child types under parents 5 - Set a rule for a child type 6 - Note it displays as Parent->Child 7 - Have three child types under a parent 8 - Set the parent 'Current checkouts allowed' to 3 9 - Set the children 'Current checkouts allowed' to: type1 = 2 type2 = 1 type3 = 1 10 - Create some items of the type above 11 - Note you can checkout 2 of type 1, and not 3 12 - Note you can checkout 1 of type 2, but not 2 13 - Note that you now cannot checkout any of type3 14 - Note you cannot checkout any of the parent type 15 - Return one of the other items and note you can now checkout an item of type3 16 - Return another item and note you can checkout an item of the parent type 17 - Return all 18 - Set the parent type to 1 19 - Now note you can only checkout 1 of any of the children 20 - Set the parent to 0 21 - Note you cannot checkout any of the child types Signed-off-by: Liz Rea <wizzyrea@gmail.com> Signed-off-by: Lisette Scheer <lisetteslatah@gmail.com> Signed-off-by: Alex Arnaud <alex.arnaud@biblibre.com> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
This commit is contained in:
parent
453fd5ce90
commit
7ebc85da16
4 changed files with 92 additions and 66 deletions
|
@ -153,6 +153,17 @@ sub parent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head3 children_with_localization
|
||||||
|
|
||||||
|
Returns the ItemType objects of the children of this type or undef.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub children_with_localization {
|
||||||
|
my ( $self ) = @_;
|
||||||
|
return Koha::ItemTypes->search_with_localization({ parent_type => $self->itemtype });
|
||||||
|
}
|
||||||
|
|
||||||
=head3 type
|
=head3 type
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
|
@ -25,9 +25,12 @@ use base qw( Template::Plugin );
|
||||||
use Koha::ItemTypes;
|
use Koha::ItemTypes;
|
||||||
|
|
||||||
sub GetDescription {
|
sub GetDescription {
|
||||||
my ( $self, $itemtypecode ) = @_;
|
my ( $self, $itemtypecode, $want_parent ) = @_;
|
||||||
my $itemtype = Koha::ItemTypes->find( $itemtypecode );
|
my $itemtype = Koha::ItemTypes->find( $itemtypecode );
|
||||||
return $itemtype ? $itemtype->translated_description : q{};
|
return q{} unless $itemtype;
|
||||||
|
my $parent;
|
||||||
|
$parent = $itemtype->parent if $want_parent;
|
||||||
|
return $parent ? $parent->translated_description . "->" . $itemtype->translated_description : $itemtype->translated_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub Get {
|
sub Get {
|
||||||
|
|
|
@ -57,6 +57,10 @@
|
||||||
<li>default (all libraries), all patron categories, same item type</li>
|
<li>default (all libraries), all patron categories, same item type</li>
|
||||||
<li>default (all libraries), all patron categories, all item types</li>
|
<li>default (all libraries), all patron categories, all item types</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p>Where an itemtype has a parent, the rule will display as "Parent->Child" and the number of
|
||||||
|
current checkouts allowed will be limited to either the maximum for the parent (counting sibling types)
|
||||||
|
or the specific rule's type, whichever is less.</p>
|
||||||
<p>To modify a rule, create a new one with the same patron category and item type.</p>
|
<p>To modify a rule, create a new one with the same patron category and item type.</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -86,7 +90,9 @@
|
||||||
<table id="default-circulation-rules">
|
<table id="default-circulation-rules">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th> </th>
|
||||||
<th>Patron category</th>
|
<th>Patron category</th>
|
||||||
|
<th> </th>
|
||||||
<th>Item type</th>
|
<th>Item type</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
<th>Note</th>
|
<th>Note</th>
|
||||||
|
@ -162,6 +168,7 @@
|
||||||
[% IF show_rule %]
|
[% IF show_rule %]
|
||||||
[% SET row_count = row_count + 1 %]
|
[% SET row_count = row_count + 1 %]
|
||||||
<tr row_countd="row_[% row_count | html %]">
|
<tr row_countd="row_[% row_count | html %]">
|
||||||
|
<td>[% IF ( c == undef ) %]1[% ELSE %]0[% END %]</td>
|
||||||
<td>
|
<td>
|
||||||
[% IF c == undef %]
|
[% IF c == undef %]
|
||||||
<em>All</em>
|
<em>All</em>
|
||||||
|
@ -169,11 +176,12 @@
|
||||||
[% Categories.GetName(c) | html %]
|
[% Categories.GetName(c) | html %]
|
||||||
[% END %]
|
[% END %]
|
||||||
</td>
|
</td>
|
||||||
|
<td>[% IF ( i == undef ) %]1[% ELSE %]0[% END %]</td>
|
||||||
<td>
|
<td>
|
||||||
[% IF i == undef %]
|
[% IF i == undef %]
|
||||||
<em>All</em>
|
<em>All</em>
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
[% ItemTypes.GetDescription(i) | html %]
|
[% ItemTypes.GetDescription(i,1) | html %]
|
||||||
[% END %]
|
[% END %]
|
||||||
</td>
|
</td>
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
|
@ -321,6 +329,7 @@
|
||||||
[% END %]
|
[% END %]
|
||||||
[% END %]
|
[% END %]
|
||||||
<tr id="edit_row">
|
<tr id="edit_row">
|
||||||
|
<td>2</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="categorycode" id="categorycode">
|
<select name="categorycode" id="categorycode">
|
||||||
<option value="*">All</option>
|
<option value="*">All</option>
|
||||||
|
@ -329,11 +338,21 @@
|
||||||
[% END %]
|
[% END %]
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
<td>0</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="itemtype" id="matrixitemtype" style="width:13em;">
|
<select name="itemtype" id="matrixitemtype" style="width:13em;">
|
||||||
<option value="*">All</option>
|
<option value="*">All</option>
|
||||||
[% FOREACH itemtypeloo IN itemtypeloop %]
|
[% FOREACH itemtypeloo IN itemtypeloop %]
|
||||||
|
[% NEXT IF itemtypeloo.parent_type %]
|
||||||
<option value="[% itemtypeloo.itemtype | html %]">[% itemtypeloo.translated_description | html %]</option>
|
<option value="[% itemtypeloo.itemtype | html %]">[% itemtypeloo.translated_description | html %]</option>
|
||||||
|
[% SET children = itemtypeloo.children_with_localization %]
|
||||||
|
[% IF children %]
|
||||||
|
<optgroup>
|
||||||
|
[% FOREACH child IN children %]
|
||||||
|
<option value="[% child.itemtype | html %]">[% child.translated_description | html %]</option>
|
||||||
|
[% END %]
|
||||||
|
</optgroup>
|
||||||
|
[% END %]
|
||||||
[% END %]
|
[% END %]
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
@ -432,7 +451,9 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th> </th>
|
||||||
<th>Patron category</th>
|
<th>Patron category</th>
|
||||||
|
<th> </th>
|
||||||
<th>Item type</th>
|
<th>Item type</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>Note</th>
|
<th>Note</th>
|
||||||
|
@ -943,8 +964,20 @@
|
||||||
|
|
||||||
[% MACRO jsinclude BLOCK %]
|
[% MACRO jsinclude BLOCK %]
|
||||||
[% Asset.js("js/admin-menu.js") | $raw %]
|
[% Asset.js("js/admin-menu.js") | $raw %]
|
||||||
|
[% INCLUDE 'datatables.inc' %]
|
||||||
[% INCLUDE 'calendar.inc' %]
|
[% INCLUDE 'calendar.inc' %]
|
||||||
<script>
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("#default-circulation-rules").dataTable($.extend(true,{},dataTablesDefaults, {
|
||||||
|
"aoColumnDefs": [
|
||||||
|
{ "bVisible": false, "aTargets": [ 0,2 ] },
|
||||||
|
{ "bSortable": false, "aTargets": ["_all"] }
|
||||||
|
],
|
||||||
|
"aaSortingFixed": [ [0,'asc'], [1,'asc'], [2,'asc'], [3,'asc'] ],
|
||||||
|
"bPaginate": false,
|
||||||
|
"bAutoWidth": false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
function clear_edit(){
|
function clear_edit(){
|
||||||
var cancel = confirm(_("Are you sure you want to cancel your changes?"));
|
var cancel = confirm(_("Are you sure you want to cancel your changes?"));
|
||||||
|
@ -1030,6 +1063,7 @@
|
||||||
// select the corresponding option
|
// select the corresponding option
|
||||||
$(current_column).find("select option").each(function(){
|
$(current_column).find("select option").each(function(){
|
||||||
opt = $(this).text().toLowerCase();
|
opt = $(this).text().toLowerCase();
|
||||||
|
itm = itm.replace(/.*->(.*)/,"$1"); //If item type is part of a group we need to clear the parent description
|
||||||
opt = opt.replace(/^\s*|\s*$/g,'');
|
opt = opt.replace(/^\s*|\s*$/g,'');
|
||||||
if ( opt == itm.toLowerCase() ) {
|
if ( opt == itm.toLowerCase() ) {
|
||||||
$(this).attr('selected', 'selected');
|
$(this).attr('selected', 'selected');
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
use Modern::Perl;
|
use Modern::Perl;
|
||||||
|
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Test::More tests => 24;
|
use Test::More tests => 13;
|
||||||
|
|
||||||
use t::lib::Mocks;
|
use t::lib::Mocks;
|
||||||
use t::lib::TestBuilder;
|
use t::lib::TestBuilder;
|
||||||
|
@ -40,54 +40,37 @@ BEGIN {
|
||||||
my $database = Koha::Database->new();
|
my $database = Koha::Database->new();
|
||||||
my $schema = $database->schema();
|
my $schema = $database->schema();
|
||||||
$schema->txn_begin;
|
$schema->txn_begin;
|
||||||
Koha::ItemTypes->delete;
|
|
||||||
|
|
||||||
Koha::ItemType->new(
|
my $builder = t::lib::TestBuilder->new;
|
||||||
{
|
my $initial_count = Koha::ItemTypes->search->count;
|
||||||
itemtype => 'type1',
|
|
||||||
description => 'description',
|
|
||||||
rentalcharge => '0.00',
|
|
||||||
imageurl => 'imageurl',
|
|
||||||
summary => 'summary',
|
|
||||||
checkinmsg => 'checkinmsg',
|
|
||||||
checkinmsgtype => 'checkinmsgtype',
|
|
||||||
processfee => '0.00',
|
|
||||||
defaultreplacecost => '0.00',
|
|
||||||
}
|
|
||||||
)->store;
|
|
||||||
|
|
||||||
Koha::ItemType->new(
|
my $parent1 = $builder->build_object({ class => 'Koha::ItemTypes', value => { description => 'description' } });
|
||||||
{
|
my $child1 = $builder->build_object({
|
||||||
itemtype => 'type2',
|
class => 'Koha::ItemTypes',
|
||||||
description => 'description',
|
value => {
|
||||||
rentalcharge => '0.00',
|
parent_type => $parent1->itemtype,
|
||||||
imageurl => 'imageurl',
|
description => 'description',
|
||||||
summary => 'summary',
|
}
|
||||||
checkinmsg => 'checkinmsg',
|
});
|
||||||
checkinmsgtype => 'checkinmsgtype',
|
my $child2 = $builder->build_object({
|
||||||
processfee => '0.00',
|
class => 'Koha::ItemTypes',
|
||||||
defaultreplacecost => '0.00',
|
value => {
|
||||||
}
|
parent_type => $parent1->itemtype,
|
||||||
)->store;
|
description => 'description',
|
||||||
|
}
|
||||||
Koha::ItemType->new(
|
});
|
||||||
{
|
my $child3 = $builder->build_object({
|
||||||
itemtype => 'type3',
|
class => 'Koha::ItemTypes',
|
||||||
description => 'description',
|
value => {
|
||||||
rentalcharge => '0.00',
|
parent_type => $parent1->itemtype,
|
||||||
imageurl => 'imageurl',
|
description => 'description',
|
||||||
summary => 'summary',
|
}
|
||||||
checkinmsg => 'checkinmsg',
|
});
|
||||||
checkinmsgtype => 'checkinmsgtype',
|
|
||||||
processfee => '0.00',
|
|
||||||
defaultreplacecost => '0.00',
|
|
||||||
}
|
|
||||||
)->store;
|
|
||||||
|
|
||||||
Koha::Localization->new(
|
Koha::Localization->new(
|
||||||
{
|
{
|
||||||
entity => 'itemtypes',
|
entity => 'itemtypes',
|
||||||
code => 'type1',
|
code => $child1->itemtype,
|
||||||
lang => 'en',
|
lang => 'en',
|
||||||
translation => 'b translated itemtype desc'
|
translation => 'b translated itemtype desc'
|
||||||
}
|
}
|
||||||
|
@ -95,7 +78,7 @@ Koha::Localization->new(
|
||||||
Koha::Localization->new(
|
Koha::Localization->new(
|
||||||
{
|
{
|
||||||
entity => 'itemtypes',
|
entity => 'itemtypes',
|
||||||
code => 'type2',
|
code => $child2->itemtype,
|
||||||
lang => 'en',
|
lang => 'en',
|
||||||
translation => 'a translated itemtype desc'
|
translation => 'a translated itemtype desc'
|
||||||
}
|
}
|
||||||
|
@ -103,36 +86,24 @@ Koha::Localization->new(
|
||||||
Koha::Localization->new(
|
Koha::Localization->new(
|
||||||
{
|
{
|
||||||
entity => 'something_else',
|
entity => 'something_else',
|
||||||
code => 'type2',
|
code => $child2->itemtype,
|
||||||
lang => 'en',
|
lang => 'en',
|
||||||
translation => 'another thing'
|
translation => 'another thing'
|
||||||
}
|
}
|
||||||
)->store;
|
)->store;
|
||||||
|
|
||||||
my $type = Koha::ItemTypes->find('type1');
|
my $type = Koha::ItemTypes->find($child1->itemtype);
|
||||||
ok( defined($type), 'first result' );
|
ok( defined($type), 'first result' );
|
||||||
is( $type->itemtype, 'type1', 'itemtype/code' );
|
is_deeply( $type->unblessed, $child1->unblessed, "We got back the same object" );
|
||||||
is( $type->description, 'description', 'description' );
|
|
||||||
is( $type->rentalcharge+0, 0, 'rentalcharge' );
|
|
||||||
is( $type->imageurl, 'imageurl', 'imageurl' );
|
|
||||||
is( $type->summary, 'summary', 'summary' );
|
|
||||||
is( $type->checkinmsg, 'checkinmsg', 'checkinmsg' );
|
|
||||||
is( $type->checkinmsgtype, 'checkinmsgtype', 'checkinmsgtype' );
|
|
||||||
|
|
||||||
$type = Koha::ItemTypes->find('type2');
|
$type = Koha::ItemTypes->find($child2->itemtype);
|
||||||
ok( defined($type), 'second result' );
|
ok( defined($type), 'second result' );
|
||||||
is( $type->itemtype, 'type2', 'itemtype/code' );
|
is_deeply( $type->unblessed, $child2->unblessed, "We got back the same object" );
|
||||||
is( $type->description, 'description', 'description' );
|
|
||||||
is( $type->rentalcharge+0, 0, 'rentalcharge' );
|
|
||||||
is( $type->imageurl, 'imageurl', 'imageurl' );
|
|
||||||
is( $type->summary, 'summary', 'summary' );
|
|
||||||
is( $type->checkinmsg, 'checkinmsg', 'checkinmsg' );
|
|
||||||
is( $type->checkinmsgtype, 'checkinmsgtype', 'checkinmsgtype' );
|
|
||||||
|
|
||||||
t::lib::Mocks::mock_preference('language', 'en');
|
t::lib::Mocks::mock_preference('language', 'en');
|
||||||
t::lib::Mocks::mock_preference('opaclanguages', 'en');
|
t::lib::Mocks::mock_preference('opaclanguages', 'en');
|
||||||
my $itemtypes = Koha::ItemTypes->search_with_localization;
|
my $itemtypes = Koha::ItemTypes->search_with_localization;
|
||||||
is( $itemtypes->count, 3, 'There are 3 item types' );
|
is( $itemtypes->count, $initial_count + 4, 'We added 4 item types' );
|
||||||
my $first_itemtype = $itemtypes->next;
|
my $first_itemtype = $itemtypes->next;
|
||||||
is(
|
is(
|
||||||
$first_itemtype->translated_description,
|
$first_itemtype->translated_description,
|
||||||
|
@ -140,7 +111,14 @@ is(
|
||||||
'item types should be sorted by translated description'
|
'item types should be sorted by translated description'
|
||||||
);
|
);
|
||||||
|
|
||||||
my $builder = t::lib::TestBuilder->new;
|
my $children = $parent1->children_with_localization;
|
||||||
|
my $first_child = $children->next;
|
||||||
|
is(
|
||||||
|
$first_child->translated_description,
|
||||||
|
'a translated itemtype desc',
|
||||||
|
'item types should be sorted by translated description'
|
||||||
|
);
|
||||||
|
|
||||||
my $item_type = $builder->build_object({ class => 'Koha::ItemTypes' });
|
my $item_type = $builder->build_object({ class => 'Koha::ItemTypes' });
|
||||||
|
|
||||||
is( $item_type->can_be_deleted, 1, 'An item type that is not used can be deleted');
|
is( $item_type->can_be_deleted, 1, 'An item type that is not used can be deleted');
|
||||||
|
|
Loading…
Reference in a new issue