Bugzilla – Attachment 168120 Details for
Bug 35906
Add bookable option on itemtypes
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 35906: Add bookable option on itemtype
Bug-35906-Add-bookable-option-on-itemtype.patch (text/plain), 22.33 KB, created by
Thibaud Guillot (thibaud_g)
on 2024-06-26 08:11:43 UTC
(
hide
)
Description:
Bug 35906: Add bookable option on itemtype
Filename:
MIME Type:
Creator:
Thibaud Guillot (thibaud_g)
Created:
2024-06-26 08:11:43 UTC
Size:
22.33 KB
patch
obsolete
>From 55ba29ab62c6b6ef6e69adfee83a5d7205b9f038 Mon Sep 17 00:00:00 2001 >From: Thibaud Guillot <thibaud.guillot@biblibre.com> >Date: Thu, 25 Jan 2024 14:14:07 +0100 >Subject: [PATCH] Bug 35906: Add bookable option on itemtype >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Actually new "booking" feature can be set on an item but no directly on >an itemtype. This patch adds this possibility. > >Test plan: >1) Test this new feature on an item as it were currently working. >2) Apply this patch >3) Run updatedatabase.pl >4) Reload Schema by running update_dbix_class_files.pl >5) Edit an itemtype, there is a new checkbox to add 'bookable' option >6) Test it with item with this itemtype, if there is 1 item at least you > will see 'Booking' tab. >7) You now have a new setting on the item bookable option to 'Follow > itemtype" > >Sponsored by: Association de Gestion des Ã…uvres Sociales d'Inria (AGOS) >--- > Koha/Item.pm | 12 +++++++ > Koha/ItemType.pm | 1 + > Koha/Items.pm | 12 +++++-- > Koha/REST/V1/Biblios.pm | 3 ++ > admin/itemtypes.pl | 3 ++ > api/v1/swagger/definitions/item.yaml | 14 ++++++-- > api/v1/swagger/paths/biblios.yaml | 1 + > api/v1/swagger/paths/items.yaml | 4 ++- > catalogue/updateitem.pl | 3 +- > .../Bug_35906_add-column-bookable-itemtype.pl | 35 +++++++++++++++++++ > installer/data/mysql/kohastructure.sql | 5 +-- > .../prog/en/modules/admin/itemtypes.tt | 9 +++++ > .../prog/en/modules/catalogue/moredetail.tt | 19 +++++++--- > t/db_dependent/Koha/Items.t | 27 ++++++++++---- > t/lib/TestBuilder.pm | 1 + > 15 files changed, 128 insertions(+), 21 deletions(-) > create mode 100755 installer/data/mysql/atomicupdate/Bug_35906_add-column-bookable-itemtype.pl > >diff --git a/Koha/Item.pm b/Koha/Item.pm >index 91c15b22d55..03e2d0794ad 100644 >--- a/Koha/Item.pm >+++ b/Koha/Item.pm >@@ -1830,6 +1830,18 @@ that this complicates filtering, but some query trickery could do it in the cont > > sub item_type { > return shift->itemtype; >+=head3 effective_bookable >+ >+ my $bookable = $item->effective_bookable; >+ >+Returns the effective bookability of the current item, be that item or itemtype level >+ >+=cut >+ >+sub effective_bookable { >+ my ($self) = @_; >+ >+ return defined( $self->bookable ) ? $self->bookable : $self->itemtype->bookable; > } > > =head3 orders >diff --git a/Koha/ItemType.pm b/Koha/ItemType.pm >index fb88518c257..f4c0c4aee98 100644 >--- a/Koha/ItemType.pm >+++ b/Koha/ItemType.pm >@@ -234,6 +234,7 @@ sub to_api_mapping { > rentalcharge_daily_calendar => 'daily_rental_charge_calendar', > rentalcharge_hourly => 'hourly_rental_charge', > rentalcharge_hourly_calendar => 'hourly_rental_charge_calendar', >+ bookable_itemtype => 'bookable_itemtype', > }; > } > >diff --git a/Koha/Items.pm b/Koha/Items.pm >index 7c594e6798e..40cc5809a65 100644 >--- a/Koha/Items.pm >+++ b/Koha/Items.pm >@@ -169,9 +169,17 @@ Returns a new resultset, containing only those items that are allowed to be book > sub filter_by_bookable { > my ($self) = @_; > >- my $params = { bookable => 1 }; >+ my @bookable_itemtypes = Koha::ItemTypes->search( { bookable => 1 } )->get_column('itemtype'); > >- return $self->search($params); >+ my ( $where, $attr ); >+ if ( C4::Context->preference("item-level_itypes") ) { >+ $where = [ { bookable => 1 }, { bookable => undef, itype => { -in => \@bookable_itemtypes } } ]; >+ } else { >+ $where = [ { bookable => 1 }, { bookable => undef, 'biblioitem.itemtype' => { -in => \@bookable_itemtypes } } ]; >+ $attr = { join => 'biblioitem' }; >+ } >+ >+ return $self->search( $where, $attr ); > } > > =head3 move_to_biblio >diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm >index 1c15e8ec25b..ae4d5d1c26c 100644 >--- a/Koha/REST/V1/Biblios.pm >+++ b/Koha/REST/V1/Biblios.pm >@@ -266,6 +266,9 @@ sub get_items { > my $biblio = Koha::Biblios->find( { biblionumber => $c->param('biblio_id') }, { prefetch => ['items'] } ); > my $bookable_only = $c->param('bookable'); > >+ # Deletion of parameter to avoid filtering on the items table in the case of bookings on 'itemtype' >+ $c->req->params->remove('bookable'); >+ > return $c->render_resource_not_found("Bibliographic record") > unless $biblio; > >diff --git a/admin/itemtypes.pl b/admin/itemtypes.pl >index 684f9377dbc..b5dbc7c98c6 100755 >--- a/admin/itemtypes.pl >+++ b/admin/itemtypes.pl >@@ -98,6 +98,7 @@ if ( $op eq 'add_form' ) { > my $rentalcharge_daily_calendar = $input->param('rentalcharge_daily_calendar') // 0; > my $rentalcharge_hourly_calendar = $input->param('rentalcharge_hourly_calendar') // 0; > my $automatic_checkin = $input->param('automatic_checkin') // 0; >+ my $bookable = $input->param('bookable')// 0; > > if ( $itemtype and $is_a_modif ) { # it's a modification > $itemtype->description($description); >@@ -118,6 +119,7 @@ if ( $op eq 'add_form' ) { > $itemtype->rentalcharge_daily_calendar($rentalcharge_daily_calendar); > $itemtype->rentalcharge_hourly_calendar($rentalcharge_hourly_calendar); > $itemtype->automatic_checkin($automatic_checkin); >+ $itemtype->bookable($bookable); > > eval { > $itemtype->store; >@@ -151,6 +153,7 @@ if ( $op eq 'add_form' ) { > rentalcharge_daily_calendar => $rentalcharge_daily_calendar, > rentalcharge_hourly_calendar => $rentalcharge_hourly_calendar, > automatic_checkin => $automatic_checkin, >+ bookable => $bookable, > } > ); > eval { >diff --git a/api/v1/swagger/definitions/item.yaml b/api/v1/swagger/definitions/item.yaml >index ba1a8a890e1..f44b3d5ae3c 100644 >--- a/api/v1/swagger/definitions/item.yaml >+++ b/api/v1/swagger/definitions/item.yaml >@@ -24,6 +24,11 @@ properties: > - "null" > description: Information about the acquisition source (it is not really a vendor id) > bookable: >+ type: >+ - boolean >+ - "null" >+ description: Item level bookability override. >+ effective_bookable: > type: boolean > description: Allow bookings on this item. > home_library_id: >@@ -90,7 +95,8 @@ properties: > - string > - "null" > format: date-time >- description: The date and time an item was last marked as withdrawn, NULL if not >+ description: >+ The date and time an item was last marked as withdrawn, NULL if not > withdrawn > callnumber: > type: >@@ -155,14 +161,16 @@ properties: > type: > - string > - "null" >- description: Linked to the CART and PROC temporary locations feature, stores the >+ description: >+ Linked to the CART and PROC temporary locations feature, stores the > permanent shelving location > checked_out_date: > type: > - string > - "null" > format: date >- description: Defines if item is checked out (NULL for not checked out, and checkout >+ description: >+ Defines if item is checked out (NULL for not checked out, and checkout > date for checked out) > call_number_source: > type: >diff --git a/api/v1/swagger/paths/biblios.yaml b/api/v1/swagger/paths/biblios.yaml >index 2da90e27d62..09ef006d749 100644 >--- a/api/v1/swagger/paths/biblios.yaml >+++ b/api/v1/swagger/paths/biblios.yaml >@@ -450,6 +450,7 @@ > - cover_image_ids > - item_group_item.item_group.description > - serial_item.serial >+ - effective_bookable > collectionFormat: csv > - $ref: "../swagger.yaml#/parameters/match" > - $ref: "../swagger.yaml#/parameters/order_by" >diff --git a/api/v1/swagger/paths/items.yaml b/api/v1/swagger/paths/items.yaml >index 3f4677a9a35..12edb8eb3af 100644 >--- a/api/v1/swagger/paths/items.yaml >+++ b/api/v1/swagger/paths/items.yaml >@@ -22,6 +22,7 @@ > enum: > - +strings > - biblio >+ - effective_bookable > collectionFormat: csv > - $ref: "../swagger.yaml#/parameters/match" > - $ref: "../swagger.yaml#/parameters/order_by" >@@ -81,6 +82,7 @@ > type: string > enum: > - +strings >+ - effective_bookable > collectionFormat: csv > consumes: > - application/json >@@ -150,7 +152,7 @@ > * linked_analytics: The item has linked analytic records > * not_same_branch: The item is blocked by independent branches > schema: >- $ref: "../swagger.yaml#/definitions/error" >+ $ref: "../swagger.yaml#/definitions/error" > "500": > description: | > Internal server error. Possible `error_code` attribute values: >diff --git a/catalogue/updateitem.pl b/catalogue/updateitem.pl >index 5b092763dfa..087dd98f8aa 100755 >--- a/catalogue/updateitem.pl >+++ b/catalogue/updateitem.pl >@@ -39,7 +39,7 @@ my $itemnotes_nonpublic=$cgi->param('itemnotes_nonpublic'); > my $withdrawn=$cgi->param('withdrawn'); > my $damaged=$cgi->param('damaged'); > my $exclude_from_local_holds_priority = $cgi->param('exclude_from_local_holds_priority'); >-my $bookable = $cgi->param('bookable'); >+my $bookable = $cgi->param('bookable') // q{}; > > my $confirm=$cgi->param('confirm'); > my $dbh = C4::Context->dbh; >@@ -77,6 +77,7 @@ elsif ( $op eq "cud-set_public_note" ) { # i.e., itemnotes parameter passed from > $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority); > $messages = "updated_exclude_from_local_holds_priority=$exclude_from_local_holds_priority&"; > } elsif ( $op eq "cud-set_bookable" && $bookable ne $item_data_hashref->{'bookable'} ) { >+ undef($bookable) if $bookable eq ""; > $item->bookable($bookable); > } elsif ( $op eq "cud-set_damaged" && $damaged ne $item_data_hashref->{'damaged'}) { > $item->damaged($damaged); >diff --git a/installer/data/mysql/atomicupdate/Bug_35906_add-column-bookable-itemtype.pl b/installer/data/mysql/atomicupdate/Bug_35906_add-column-bookable-itemtype.pl >new file mode 100755 >index 00000000000..a289e189ae7 >--- /dev/null >+++ b/installer/data/mysql/atomicupdate/Bug_35906_add-column-bookable-itemtype.pl >@@ -0,0 +1,35 @@ >+use Modern::Perl; >+ >+return { >+ bug_number => "35906", >+ description => "Add bookable column on itemtypes table", >+ up => sub { >+ my ($args) = @_; >+ my ( $dbh, $out ) = @$args{qw(dbh out)}; >+ >+ $dbh->do( >+ q{ >+ ALTER TABLE itemtypes ADD IF NOT EXISTS bookable INT(1) DEFAULT 0 >+ } >+ ); >+ >+ say $out "Added column 'itemtypes.bookable'"; >+ >+ $dbh->do( >+ q{ >+ ALTER TABLE items MODIFY COLUMN bookable tinyint(1) DEFAULT NULL COMMENT 'nullable boolean value defining whether this this item is available for bookings or not' >+ } >+ ); >+ >+ say $out "Updated column 'items.bookable' allow nullable"; >+ >+ $dbh->do( >+ q{ >+ ALTER TABLE deleteditems MODIFY COLUMN bookable tinyint(1) DEFAULT NULL COMMENT 'nullable boolean value defining whether this this item is available for bookings or not' >+ } >+ ); >+ >+ say $out "Updated column 'deleteditems.bookable' allow nullable"; >+ >+ }, >+}; >diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql >index 14cef909b3a..6472990f531 100644 >--- a/installer/data/mysql/kohastructure.sql >+++ b/installer/data/mysql/kohastructure.sql >@@ -2750,7 +2750,7 @@ CREATE TABLE `deleteditems` ( > `biblionumber` int(11) NOT NULL DEFAULT 0 COMMENT 'foreign key from biblio table used to link this item to the right bib record', > `biblioitemnumber` int(11) NOT NULL DEFAULT 0 COMMENT 'foreign key from the biblioitems table to link to item to additional information', > `barcode` varchar(20) DEFAULT NULL COMMENT 'item barcode (MARC21 952$p)', >- `bookable` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'boolean value defining whether this this item is available for bookings or not', >+ `bookable` tinyint(1) DEFAULT NULL COMMENT 'nullable boolean value defining whether this this item is available for bookings or not', > `dateaccessioned` date DEFAULT NULL COMMENT 'date the item was acquired or added to Koha (MARC21 952$d)', > `booksellerid` longtext DEFAULT NULL COMMENT 'where the item was purchased (MARC21 952$e)', > `homebranch` varchar(10) DEFAULT NULL COMMENT 'foreign key from the branches table for the library that owns this item (MARC21 952$a)', >@@ -4008,7 +4008,7 @@ CREATE TABLE `items` ( > `biblionumber` int(11) NOT NULL DEFAULT 0 COMMENT 'foreign key from biblio table used to link this item to the right bib record', > `biblioitemnumber` int(11) NOT NULL DEFAULT 0 COMMENT 'foreign key from the biblioitems table to link to item to additional information', > `barcode` varchar(20) DEFAULT NULL COMMENT 'item barcode (MARC21 952$p)', >- `bookable` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'boolean value defining whether this this item is available for bookings or not', >+ `bookable` tinyint(1) DEFAULT NULL COMMENT 'nullable boolean value defining whether this this item is available for bookings or not', > `dateaccessioned` date DEFAULT NULL COMMENT 'date the item was acquired or added to Koha (MARC21 952$d)', > `booksellerid` longtext DEFAULT NULL COMMENT 'where the item was purchased (MARC21 952$e)', > `homebranch` varchar(10) DEFAULT NULL COMMENT 'foreign key from the branches table for the library that owns this item (MARC21 952$a)', >@@ -4137,6 +4137,7 @@ CREATE TABLE `itemtypes` ( > `hideinopac` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Hide the item type from the search options in OPAC', > `searchcategory` varchar(80) DEFAULT NULL COMMENT 'Group this item type with others with the same value on OPAC search options', > `automatic_checkin` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'If automatic checkin is enabled for items of this type', >+ `bookable` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Activate bookable feature for items related to this item type', > PRIMARY KEY (`itemtype`), > UNIQUE KEY `itemtype` (`itemtype`), > KEY `itemtypes_ibfk_1` (`parent_type`), >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt >index b9ea060e304..707237449ac 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt >@@ -258,6 +258,15 @@ > [% END %] > <span class="hint">If checked, items will be automatically checked in once they've reached their due date. This feature requires the <code>misc/cronjobs/automatic_checkin.pl</code> cronjob. Ask your system administrator to schedule it.</span> > </li> >+ <li> >+ <label for="bookable">Bookable: </label> >+ [% IF itemtype.bookable %] >+ <input type="checkbox" id="bookable" name="bookable" checked="checked" value="1" /> >+ [% ELSE %] >+ <input type="checkbox" id="bookable" name="bookable" value="1" /> >+ [% END %] >+ <span class="hint">If checked, all items of this type will be enabled for future bookings unless overridden at the item level.</span> >+ </li> > <li> > <label for="rentalcharge">Rental charge: </label> > <input type="text" id="rentalcharge" name="rentalcharge" size="10" value="[% itemtype.rentalcharge | $Price on_editing => 1 %]" min="0" /> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >index 6ef020cecbd..91e18e13dac 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >@@ -350,7 +350,9 @@ > </form> > </li> > <li> >- <span class="label">Bookable:</span> >+ <span class="label"> >+ Bookable: >+ </span> > [% IF ( CAN_user_circulate ) %] > <form action="updateitem.pl" method="post"> > [% INCLUDE 'csrf-token.inc' %] >@@ -358,20 +360,27 @@ > <input type="hidden" name="biblioitemnumber" value="[% ITEM_DAT.biblioitemnumber | html %]" /> > <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber | html %]" /> > <select name="bookable"> >- [% IF ITEM_DAT.bookable == 1 %] >+ [% IF ITEM_DAT.bookable == 1 %] >+ <option value="">Follow item type</option> > <option value="1" selected="selected">Yes</option> > <option value="0">No</option> >- [% ELSE %] >+ [% ELSIF ITEM_DAT.bookable == 0 %] >+ <option value="">Follow item type</option> > <option value="1">Yes</option> > <option value="0" selected="selected">No</option> >- [% END %] >+ [% ELSE %] >+ <option value="" selected="selected">Follow item type</option> >+ <option value="1">Yes</option> >+ <option value="0">No</option> >+ [% END %] > </select> > <input type="hidden" name="op" value="cud-set_bookable" /> > <input type="submit" name="submit" class="btn btn-primary btn-xs" value="Update" /> > </form> > [% ELSE %] >- [% IF ITEM_DAT.bookable == 1 %] Yes [% ELSE %] No [% END %] >+ [% IF ITEM_DAT.bookable == 1 %] Yes [% ELSIF ITEM_DAT.bookable == 0 %] No [% ELSE %] Follow item type [% END %] > [% END %] >+ <span class="hint"> Item type bookable: [% IF ITEM_DAT.effective_itemtype.bookable == 1 %]Yes[% ELSE %]No[% END %]</span> > </li> > > </ol> <!-- /.bibliodetails --> >diff --git a/t/db_dependent/Koha/Items.t b/t/db_dependent/Koha/Items.t >index 3a0ffd11cc0..9cbf32e1e6f 100755 >--- a/t/db_dependent/Koha/Items.t >+++ b/t/db_dependent/Koha/Items.t >@@ -2078,24 +2078,37 @@ subtest 'filter_by_for_hold' => sub { > }; > > subtest 'filter_by_bookable' => sub { >- plan tests => 2; >+ plan tests => 4; > > $schema->storage->txn_begin; > >- my $biblio = $builder->build_sample_biblio; >+ t::lib::Mocks::mock_preference('item-level_itypes', 0); >+ >+ my $bookable_item_type = $builder->build_object( { class => 'Koha::ItemTypes', value => { bookable => 1 } } ); >+ my $non_bookable_item_type = $builder->build_object( { class => 'Koha::ItemTypes', value => { bookable => 0 } } ); >+ my $biblio = $builder->build_sample_biblio({ itemtype => $bookable_item_type->itemtype }); > > # bookable items >- my $bookable_item1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, bookable => 1 } ); >+ my $bookable_item1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $bookable_item_type->itemtype, bookable => 1 } ); > > # not bookable items >- my $non_bookable_item1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, bookable => 0 } ); >- my $non_bookable_item2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, bookable => 0 } ); >+ my $non_bookable_item1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $non_bookable_item_type->itemtype, bookable => 0 } ); >+ my $non_bookable_item2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $non_bookable_item_type->itemtype, bookable => 0 } ); > >- is( $biblio->items->filter_by_bookable->count, 1, "filter_by_bookable returns the correct number of items" ); >+ is( $biblio->items->filter_by_bookable->count, 1, "filter_by_bookable returns the correct number of items set at item level" ); > is( > $biblio->items->filter_by_bookable->next->itemnumber, $bookable_item1->itemnumber, >- "the correct item is returned from filter_by_bookable" >+ "the correct item is returned from filter_by_bookable at the item level" > ); > >+ $bookable_item1->bookable(undef)->store(); >+ $non_bookable_item1->bookable(undef)->store(); >+ $non_bookable_item2->bookable(undef)->store(); >+ $biblio->get_from_storage; >+ is( $biblio->items->filter_by_bookable->count, 3, "filter_by_bookable returns the correct number of items when not set at item level and using biblio level itemtypes" ); >+ >+ t::lib::Mocks::mock_preference('item-level_itypes', 1); >+ is( $biblio->items->filter_by_bookable->count, 1, "filter_by_bookable returns the correct number of items when not set at item level and using item level itemtypes" ); >+ > $schema->storage->txn_rollback; > }; >diff --git a/t/lib/TestBuilder.pm b/t/lib/TestBuilder.pm >index 57506b11d78..6a4587fe824 100644 >--- a/t/lib/TestBuilder.pm >+++ b/t/lib/TestBuilder.pm >@@ -638,6 +638,7 @@ sub _gen_default_values { > defaultreplacecost => 0, > processfee => 0, > notforloan => 0, >+ bookable => 0, > }, > Aqbookseller => { > tax_rate => 0, >-- >2.30.2
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 35906
:
161397
|
161638
|
161744
|
161961
|
162242
|
162365
|
162465
|
162466
|
162467
|
162468
|
162469
|
162470
|
162472
|
163291
|
163292
|
163293
|
163294
|
163295
|
163547
|
163548
|
163549
|
163550
|
163551
|
164579
|
164580
|
164581
|
164582
|
164583
|
164584
|
164585
|
165437
|
166866
|
166867
|
166868
|
166869
|
166870
|
166871
|
168120
|
168121
|
168122
|
168160
|
168161
|
168162
|
168163
|
168164
|
170187
|
170188
|
170189
|
170190
|
170191
|
170582
|
170583
|
170584
|
170585
|
170586
|
172127
|
172128
|
172129
|
172130
|
172131
|
172132
|
172941
|
172945
|
172950
|
172951
|
172952
|
172953
|
172954
|
172955
|
172956
|
172957
|
172958
|
172959
|
172960
|
172961
|
173324
|
173325
|
173326
|
173327
|
173328
|
173329
|
173330
|
173331
|
173332
|
173333
|
173334
|
173335
|
173893