@@ -, +, @@ information Koha::Availability::Hold Koha::Availability::Checkout | | \_________________________________/ | Koha::Availability --- Koha/Availability.pm | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++ t/Koha/Availability.t | 102 ++++++++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 Koha/Availability.pm create mode 100644 t/Koha/Availability.t --- a/Koha/Availability.pm +++ a/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->{notes}}) { + $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; --- a/t/Koha/Availability.t +++ a/t/Koha/Availability.t @@ -0,0 +1,102 @@ +#!/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 => 3; + + $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 '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; --