From 4e61301280182bb8697002d7e65705612709eb3e Mon Sep 17 00:00:00 2001 From: Jacob O'Mara Date: Wed, 15 Jan 2025 14:56:40 +0000 Subject: [PATCH] Bug 38924: Add Koha::Patron::Quota(s) classes Add initial Koha::Patron::Quota(s) classes for working with circulation quotas, including an overloaded store method to prevent overlapping quota periods. Additionally added a definition for the custom exception for if you are trying to store a quota whos active period overlaps with another for this patron --- Koha/Exceptions/Quota.pm | 18 +++++ Koha/Patron/Quota.pm | 156 +++++++++++++++++++++++++++++++++++++++ Koha/Patron/Quotas.pm | 17 +++++ 3 files changed, 191 insertions(+) create mode 100644 Koha/Exceptions/Quota.pm create mode 100644 Koha/Patron/Quota.pm create mode 100644 Koha/Patron/Quotas.pm diff --git a/Koha/Exceptions/Quota.pm b/Koha/Exceptions/Quota.pm new file mode 100644 index 00000000000..b30533848e7 --- /dev/null +++ b/Koha/Exceptions/Quota.pm @@ -0,0 +1,18 @@ +package Koha::Exceptions::Quota; + +use Modern::Perl; + +use Koha::Exceptions; + +use Exception::Class ( + 'Koha::Exceptions::Quota' => { + description => 'Base class for Quota-related exceptions', + isa => 'Koha::Exception', + }, + 'Koha::Exceptions::Quota::Clash' => { + isa => 'Koha::Exceptions::Quota', + description => 'Thrown when a quota period overlaps with an existing one', + }, +); + +1; diff --git a/Koha/Patron/Quota.pm b/Koha/Patron/Quota.pm new file mode 100644 index 00000000000..1f1da940425 --- /dev/null +++ b/Koha/Patron/Quota.pm @@ -0,0 +1,156 @@ +package Koha::Patron::Quota; + +use Modern::Perl; +use Koha::DateUtils qw( dt_from_string ); +use Koha::Exceptions::Quota; +use base qw(Koha::Object); + +=head1 NAME + +Koha::Patron::Quota - Koha Patron::Quota Object class + +=head1 API + +=head2 Class Methods + +=head3 store + +Overloaded store method to prevent creation of overlapping quota periods for a user + +=cut + +sub store { + my ($self) = @_; + + # Parse dates into DateTime objects first + my $start_dt = dt_from_string( $self->start_date ); + my $end_dt = dt_from_string( $self->end_date ); + + # Throw exception for quota period clash + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $existing_quota = Koha::Patron::Quotas->search( + { + '-and' => [ + { + '-or' => [ + start_date => { + '-between' => [ + $dtf->format_datetime($start_dt), + $dtf->format_datetime($end_dt) + ] + }, + end_date => { + '-between' => [ + $dtf->format_datetime($start_dt), + $dtf->format_datetime($end_dt) + ] + }, + { + start_date => { '<' => $dtf->format_datetime($start_dt) }, + end_date => { '>' => $dtf->format_datetime($end_dt) } + } + ] + }, + { + patron_id => $self->patron_id, + ( + $self->in_storage + ? ( id => { '!=' => $self->id } ) + : () + ), + } + ] + } + ); + Koha::Exceptions::Quota::Clash->throw() + if $existing_quota->count; + + return $self->SUPER::store(); +} + +=head3 add_to_quota + +Adds specified amount to the quota_used value + +=cut + +sub add_to_quota { + my ( $self, $amount ) = @_; + + $amount //= 1; + my $new_used = $self->used + $amount; + $self->used($new_used); + return $self->store; +} + +=head3 has_available_quota + +Returns boolean indicating if there is quota available + +=cut + +sub has_available_quota { + my ($self) = @_; + return $self->available_quota >= 1; +} + +=head3 available_quota + +Returns the amount still available in the quota + +=cut + +sub available_quota { + my ($self) = @_; + return $self->allocation - $self->used; +} + +=head2 Instance Methods + +=head3 patron + +Returns the Koha::Patron associated with this quota + +=cut + +sub patron { + my ( $self ) = @_; + return Koha::Patron->_new_from_dbic($self->_result->patron); +} + +=head3 is_active + +Returns boolean indicating if the quota period includes today's date + +=cut + +sub is_active { + my ($self) = @_; + my $today = dt_from_string; + + my $start = DateTime->new( + year => substr( $self->start_date, 0, 4 ), + month => substr( $self->start_date, 5, 2 ), + day => substr( $self->start_date, 8, 2 ) + ); + + my $end = DateTime->new( + year => substr( $self->end_date, 0, 4 ), + month => substr( $self->end_date, 5, 2 ), + day => substr( $self->end_date, 8, 2 ) + ); + + return ( $start <= $today && $end >= $today ); +} + +=head2 Internal methods + +=head3 _type + +=cut + +sub _type { + return 'PatronQuota'; +} + +1; diff --git a/Koha/Patron/Quotas.pm b/Koha/Patron/Quotas.pm new file mode 100644 index 00000000000..a3d423f2367 --- /dev/null +++ b/Koha/Patron/Quotas.pm @@ -0,0 +1,17 @@ +=head3 get_patron_quota + +Returns the active quota for a given patron + +=cut + +sub get_patron_quota { + my ( $self, $patron_id ) = @_; + + return $self->search( + { + patron_id => $patron_id, + start_date => { '<=' => \'CURRENT_DATE' }, + end_date => { '>=' => \'CURRENT_DATE' }, + } + )->single; +} -- 2.48.1