From fec85e5db221680dd7dd34d3ad867559386661a5 Mon Sep 17 00:00:00 2001 From: Lari Taskula Date: Wed, 23 Nov 2016 14:34:07 +0200 Subject: [PATCH] Bug 17712: Koha::Availability, a base for availability information This patch adds a new class, Koha::Availability, which is designed to contain availability information for each type of availability in an uniform way. Because of this, Koha::Availability has no actual availability calculation logic, but simply methods to get and set availability information into the object. With such design, we can next create new classes Koha::Availability::Hold and Koha::Availability::Checkout and let them inherit Koha::Availability so that they are able to handle availaibility information in an uniform way. Koha::Availability::Hold Koha::Availability::Checkout | | \_________________________________/ | Koha::Availability Koha::Availability can represent four levels of availability statuses: 1. available 2. available, with an additional note 3. available, but requires confirmation 4. unavailable Additional notes, reasons for a need to confirm and reasons for unavailabilities are kept in a HASHref, where each value in my proposal is a Koha::Exceptions::*. This allows us to easily store any additional data directly into the reason. For example, if we want to check biblio availability for hold and find out it is not available, the HASHref for unavailabilities has a Koha::Exceptions::Patron::Debt that contains parameters "current_outstanding" and "max_outstanding" which lets us pick up the information easily later on without making new queries. To test: 1. Run t/Koha/Availability.t Signed-off-by: Benjamin Rokseth --- Koha/Availability.pm | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++ t/Koha/Availability.t | 114 ++++++++++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 Koha/Availability.pm create mode 100644 t/Koha/Availability.t diff --git a/Koha/Availability.pm b/Koha/Availability.pm new file mode 100644 index 0000000..89d3bfe --- /dev/null +++ b/Koha/Availability.pm @@ -0,0 +1,216 @@ +package Koha::Availability; + +# Copyright Koha-Suomi Oy 2016 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Exceptions; + +=head1 NAME + +Koha::Availability - Koha Availability object class + +=head1 SYNOPSIS + +Class for storing availability information. + +=head1 DESCRIPTION + +A class to contain availability information in an uniform way. + +Koha::Availability has no actual availability calculation logic, but simply +methods to get and set availability information into the object. To get actual +availability responses for items/biblios, use e.g. Koha::Availability::Hold. + +Koha::Availability can represent four levels of availability statuses: +1. available +2. available, with an additional note +3. available, but requires confirmation +4. unavailable + +Additional notes, reasons for a need to confirm and reasons for unavailabilities +are kept in a HASHref, where each value in my proposal is a Koha::Exceptions::*. +This allows us to easily store any additional data directly into the reason. For +example, if we want to check biblio availability for hold and find out it is not +available, the HASHref for unavailabilities has a Koha::Exceptions::Patron::Debt +that contains parameters "current_outstanding" and "max_outstanding" which lets +us pick up the information easily later on without making new queries. + +With such design, Koha::Availability will be used as a parent for different types +of availabilities, like hold and checkout availability. This allows each type of +availability to perform uniformly; the ways to find out availability will be the +same and the problems with availability are represented the same way. + +Example of inheritance described above: + + Koha::Availability::Hold Koha::Availability::Checkout + | | + \_________________________________/ + | + Koha::Availability + +=head2 Class Methods + +=cut + +=head3 new + +Creates a new Koha::Availability object. + +=cut + +sub new { + my ($class, $params) = @_; + + my $self = { + available => 1, # boolean value yes / no + confirmations => {}, # needs confirmation reasons + notes => {}, # availability notes + unavailabilities => {}, # unavailability reasons + expected_available => undef, # expected availability date + }; + + bless $self, $class; + + return $self; +} + +sub AUTOLOAD { + my ($self, $params) = @_; + + my $method = our $AUTOLOAD; + $method =~ s/.*://; + + # Accessor for class parameters + if (exists $self->{$method}) { + unless (defined $params) { + return $self->{$method}; + } else { + $self->{$method} = $params; + return $self; + } + } elsif ($self->can($method)) { + $self->$method($params); + } else { + Koha::Exceptions::Object::MethodNotFound->throw( + "No method $method for " . ref($self) + ); + } +} + +=head3 confirm + +Get: $availability->confirm + Returns count of reasons that require confirmation. + To get each reason, use accessor $availability->confirmations. + +Set: $availability->confirm(Koha::Exceptions::Item::Damaged->new) + Maintains the availability status as available, and adds the given reason + into $availability->confirmations. + +=cut + +sub confirm { + my ($self, $status) = @_; + + if (!$status) { + my $keys = keys %{$self->{confirmations}}; + return $keys ? $keys : 0; + } else { + if (!keys %{$self->{unavailabilities}}) { + $self->{available} = 1; + } + my $key = ref($status); + $self->{confirmations}->{$key} = $status; + } +} + +=head3 note + +Get: $availability->note + Returns count of additional notes. + To get each reason, use accessor $availability->notes. + +Set: $availability->note(Koha::Exceptions::Item::Lost->new) + If no unavailability reasons are stored, sets availability true, and adds + given object as additional availability note. Otherwise does nothing. + +=cut + +sub note { + my ($self, $status) = @_; + + if (!$status) { + my $keys = keys %{$self->{notes}}; + return $keys ? $keys : 0; + } else { + if (!keys %{$self->{unavailabilities}}) { + $self->{available} = 1; + } + my $key = ref($status); + $self->{notes}->{$key} = $status; + } +} + +=head3 reset + +$availability->reset + +Resets availability status to available and cleans the object from any existing +notes, confirmations and unavailabilities. + +=cut + +sub reset { + my ($self) = @_; + + $self->{'available'} = 1; + $self->{'confirmations'} = {}; + $self->{'notes'} = {}; + $self->{'unavailabilities'} = {}; + + return $self; +} + +=head3 unavailable + +Accessor for unavailability. + +Get: $availability->unavailable + Returns count of reasons that make availability false. + To get all reasons, use accessor $availability->unavailabilities. + +Set: $availability->unavailable(Koha::Exceptions::Item::Withdrawn->new) + Sets availability status as "unavailable" and stores the given reason. + +=cut + +sub unavailable { + my ($self, $status) = @_; + + if (!$status) { + my $keys = keys %{$self->{unavailabilities}}; + return $keys ? $keys : 0; + } else { + $self->{available} = 0; + my $key = ref($status); + $self->{unavailabilities}->{$key} = $status; + } +} + +1; diff --git a/t/Koha/Availability.t b/t/Koha/Availability.t new file mode 100644 index 0000000..7845cf5 --- /dev/null +++ b/t/Koha/Availability.t @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +# Copyright Koha-Suomi Oy 2016 +# +# This file is part of Koha +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More tests => 2; +use t::lib::TestBuilder; + +use Koha::Exceptions; + +use_ok('Koha::Availability'); + +my $exception; + +subtest 'Instantiate new Koha::Availability -object' => sub { + plan tests => 6; + + my $availability = Koha::Availability->new; + is(ref($availability), 'Koha::Availability', 'Object instantiated successfully.'); + + subtest 'Check Koha::Availability -object default values' => \&check_default_values; + subtest 'Add additional notes' => sub { + plan tests => 4; + + $availability = Koha::Availability->new; + $exception = 'Koha::Exceptions::Exception'; + $availability->note($exception->new( error => 'Test note' )); + is($availability->note, 1, 'Added a test note with additional data.'); + is(ref($availability->notes->{$exception}), $exception, 'The object contains this test note.'); + is($availability->notes->{$exception}->error, 'Test note', 'The note contains our additional data.'); + + subtest 'Availability should stay false if it has unavailability and a note is added' => sub { + plan tests => 3; + + $availability = Koha::Availability->new; + $exception = 'Koha::Exceptions::Exception'; + $availability->unavailable($exception->new( error => 'Test unavailability' )); + is($availability->available, 0, 'Availability is false'); + $availability->note($exception->new( error => 'Test note' )); + is($availability->notes->{$exception}->error, 'Test note', 'Added a test note.'); + is($availability->available, 0, 'Availability is still false'); + }; + }; + + subtest 'Set availability to be confirmed by a librarian' => sub { + plan tests => 5; + + $availability = Koha::Availability->new; + $exception = 'Koha::Exceptions::Exception'; + $availability->confirm($exception->new( error => 'Needs to be confirmed' )); + is($availability->confirm, 1, 'Added a new exception to be confirmed by librarian, with some additional data.'); + my $confirmations = $availability->confirmations; + is(ref($confirmations->{$exception}), $exception, 'The object contains this exception.'); + is($confirmations->{$exception}->error, 'Needs to be confirmed', 'Additional data is also stored.'); + is($availability->available, 1, 'Although we have something to be confirmed, availability is still true.'); + $availability->confirm(Koha::Exceptions::MissingParameter->new( error => 'Just a test.' )); + is($availability->confirm, 2, 'Added another exception. $availability->confirm returns the correct count.'); + }; + + subtest 'Set availability to unavailable' => sub { + plan tests => 6; + + $availability = Koha::Availability->new; + $exception = 'Koha::Exceptions::Exception'; + $availability->unavailable($exception->new( error => 'Not available' )); + is($availability->unavailable, 1, 'Added a new exception with some additional data and made the availability false.'); + my $unavailabilities = $availability->unavailabilities; + is(ref($unavailabilities->{$exception}), $exception, 'The object contains this exception.'); + is($unavailabilities->{$exception}->error, 'Not available', 'Additional data is also stored.'); + is($availability->unavailable, 1, 'Availability is unavailable.'); + is($availability->available, 0, 'Not available.'); + $availability->unavailable(Koha::Exceptions::MissingParameter->new( error => 'Just a test.' )); + is($availability->unavailable, 2, 'Added another exception. $availability->confirm returns the correct count.'); + }; + + subtest 'Reset Koha::Availability -object' => sub { + plan tests => 3; + + $availability = Koha::Availability->new; + $exception = 'Koha::Exceptions::Exception'; + $availability->unavailable($exception->new( error => 'Not available' )); + ok(!$availability->available, 'Set Availability-object as unavailable.'); + ok($availability->reset, 'Object reset'); + ok($availability->available, 'Availability is now true.'); + }; +}; + +sub check_default_values { + plan tests => 5; + + my $availability = Koha::Availability->new; + is($availability->available, 1, 'Koha::Availability -object is available.'); + is(keys %{$availability->unavailabilities}, 0, 'There are no unavailabilities.'); + is(keys %{$availability->confirmations}, 0, 'Nothing needs to be confirmed.'); + is(keys %{$availability->notes}, 0, 'There are no additional notes.'); + is($availability->expected_available, undef, 'There is no expectation of future availability'); +} + +1; -- 2.1.4