Skip to content

Commit

Permalink
patron_type: implements fee amount restrictions
Browse files Browse the repository at this point in the history
Implements the fee amount limit restriction: if the fee amount limit is reached by
a patron, checkout, renewal and request operation are blocked.

Co-Authored-by: Lauren-D <laurent.dubois@itld-solutions.be>
  • Loading branch information
lauren-d authored and zannkukai committed Nov 5, 2020
1 parent 8d44d4d commit 273ab44
Show file tree
Hide file tree
Showing 9 changed files with 476 additions and 211 deletions.
6 changes: 4 additions & 2 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2251,10 +2251,12 @@ def _(x):
Location.allow_request,
Item.can_request,
CircPolicy.allow_request,
Patron.can_request
Patron.can_request,
PatronType.allow_request
],
ItemCirculationAction.EXTEND: [
Item.can_extend
Item.can_extend,
PatronType.allow_extend
],
ItemCirculationAction.CHECKOUT: [
Patron.can_checkout,
Expand Down
1 change: 1 addition & 0 deletions rero_ils/modules/items/api/circulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ def validate_request(self, current_loan, **kwargs):
}

@add_action_parameters_and_flush_indexes
@check_operation_allowed(ItemCirculationAction.EXTEND)
def extend_loan(self, current_loan, **kwargs):
"""Extend checkout duration for this item."""
loan = current_circulation.circulation.trigger(
Expand Down
16 changes: 13 additions & 3 deletions rero_ils/modules/patron_transactions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,20 @@ def create(cls, data, id_=None, delete_pid=False,
return record

@classmethod
def _build_transaction_query(cls, patron_pid, status=None):
"""Private function to build a transaction query linked to a patron."""
def _build_transaction_query(cls, patron_pid, status=None, types=None):
"""Private function to build a transaction query linked to a patron.
:param patron_pid: the patron pid being searched
:param status: (optional) array of transaction status filter,
:param types: (optional) array of transaction types filter,
:return: return prepared query.
"""
query = PatronTransactionsSearch() \
.filter('term', patron__pid=patron_pid)
if status:
query = query.filter('term', status=status)
if types:
query = query.filter('terms', type=types)
return query

@classmethod
Expand All @@ -112,16 +120,18 @@ def get_transactions_pids_for_patron(cls, patron_pid, status=None):

@classmethod
def get_transactions_total_amount_for_patron(cls, patron_pid, status=None,
types=None,
with_subscription=True):
"""Get total amount transactions linked to a patron.
:param patron_pid: the patron pid being searched
:param status: (optional) transaction status filter,
:param types: (optional) transaction type filter,
:param with_subscription: (optional) include or exclude subscription
type filter.
:return: return total amount of transactions.
"""
search = cls._build_transaction_query(patron_pid, status)
search = cls._build_transaction_query(patron_pid, status, types)
if not with_subscription:
search = search.exclude('terms', type=['subscription'])
search.aggs.metric('transactions_total_amount',
Expand Down
94 changes: 91 additions & 3 deletions rero_ils/modules/patron_types/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from ..loans.api import LoanState, get_loans_count_by_library_for_patron_pid, \
get_overdue_loan_pids
from ..minters import id_minter
from ..patron_transactions.api import PatronTransaction
from ..patrons.api import Patron, PatronsSearch
from ..patrons.utils import get_patron_from_arguments
from ..providers import Provider
Expand Down Expand Up @@ -143,14 +144,77 @@ def allow_checkout(cls, item, **kwargs):
# be relevant --> return True by default
return True, []

# check overdue items limits
patron_type = PatronType.get_record_by_pid(patron.patron_type_pid)
if not patron_type.check_overdue_items_limit(patron):
return False, [_('Checkout denied: the maximal number of overdue '
'items is reached')]

# check checkout count limit
valid, message = patron_type.check_checkout_count_limit(patron, item)
if not valid:
return False, [message]

# check fee amount limit
if not patron_type.check_fee_amount_limit(patron):
return False, [_('Checkout denied: the maximal fee amount is '
'reached')]

return True, []

@classmethod
def allow_request(cls, item, **kwargs):
"""Check if a patron type allow request item operation.
:param item : the item to check
:param kwargs : To be relevant, additional arguments should contains
'patron' argument.
:return a tuple with True|False and reasons to disallow if False.
"""
patron = get_patron_from_arguments(**kwargs)
if not patron:
# 'patron' argument are present into kwargs. This check can't
# be relevant --> return True by default
return True, []

# check overdue items limits
patron_type = PatronType.get_record_by_pid(patron.patron_type_pid)
if not patron_type.check_overdue_items_limit(patron):
return False, [_('Request denied: the maximal number of overdue '
'items is reached')]

# check fee amount limit
if not patron_type.check_fee_amount_limit(patron):
return False, [_('Request denied: the maximal fee amount is '
'reached')]

return True, []

@classmethod
def allow_extend(cls, item, **kwargs):
"""Check if a patron type allow extend loan operation.
:param item : the item to check
:param kwargs : To be relevant, additional arguments should contains
'patron' argument.
:return a tuple with True|False and reasons to disallow if False.
"""
patron = get_patron_from_arguments(**kwargs)
if not patron:
# 'patron' argument are present into kwargs. This check can't
# be relevant --> return True by default
return True, []

# check overdue items limit
patron_type = PatronType.get_record_by_pid(patron.patron_type_pid)
if not patron_type.check_overdue_items_limit(patron):
return False, [_('Renewal denied: the maximal number of overdue '
'items is reached')]

# check fee amount limit
if not patron_type.check_fee_amount_limit(patron):
return False, [_('Renewal denied: the maximal fee amount is '
'reached')]
return True, []

def get_linked_patron(self):
Expand Down Expand Up @@ -234,8 +298,8 @@ def check_checkout_count_limit(self, patron, item=None):
"""
checkout_limits = self.replace_refs().get('limits', {})\
.get('checkout_limits', {})
general_limit = checkout_limits.get('global_limit')
if not general_limit:
global_limit = checkout_limits.get('global_limit')
if not global_limit:
return True, None

# [0] get the stats fr this patron by library
Expand All @@ -244,7 +308,7 @@ def check_checkout_count_limit(self, patron, item=None):

# [1] check the general limit
patron_total_count = sum(patron_library_stats.values()) or 0
if patron_total_count >= general_limit:
if patron_total_count >= global_limit:
return False, _('Checkout denied: the maximal checkout number '
'is reached.')

Expand All @@ -266,6 +330,30 @@ def check_checkout_count_limit(self, patron, item=None):
# [4] no problem detected, checkout is allowed
return True, None

def check_fee_amount_limit(self, patron):
"""Check if a patron reached the fee amount limits.
* check the fee amount limit (if exists).
:param patron: the patron who tries to execute the checkout.
:param item: the item related to the loan.
:return a tuple of two values ::
- True|False : to know if the check is success or not.
- message(string) : the reason why the check fails.
"""
# get fee amount limit
fee_amount_limits = self.replace_refs().get('limits', {}) \
.get('fee_amount_limits', {})
default_limit = fee_amount_limits.get('default_value')
if default_limit:
# get total amount for open transactions on overdue and without
# subscription fee
patron_total_amount = PatronTransaction. \
get_transactions_total_amount_for_patron(
patron.pid, status='open', types=['overdue'],
with_subscription=False)
return patron_total_amount < default_limit
return True


class PatronTypesIndexer(IlsRecordsIndexer):
"""Holdings indexing class."""
Expand Down
8 changes: 8 additions & 0 deletions rero_ils/modules/patrons/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,14 @@ def get_circulation_messages(self):
'type': 'error',
'content': message
})
# check fee amount limit
valid = patron_type.check_fee_amount_limit(self)
if not valid:
messages.append({
'type': 'error',
'content': _(
'Transactions denied: the maximal fee amount is reached.')
})

return messages

Expand Down
5 changes: 3 additions & 2 deletions rero_ils/modules/patrons/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ def user_has_patron(user=current_user):
def get_patron_from_arguments(**kwargs):
"""Try to load a patron from potential arguments."""
from .api import Patron
required_arguments = ['patron', 'patron_barcode', 'patron_pid']
required_arguments = ['patron', 'patron_barcode', 'patron_pid', 'loan']
if not any(k in required_arguments for k in kwargs):
return None
return kwargs.get('patron') \
or Patron.get_patron_by_barcode(kwargs.get('patron_barcode')) \
or Patron.get_record_by_pid(kwargs.get('patron_pid'))
or Patron.get_record_by_pid(kwargs.get('patron_pid')) \
or Patron.get_record_by_pid(kwargs.get('loan').get('patron_id'))
Loading

0 comments on commit 273ab44

Please sign in to comment.