diff --git a/loanpy/src/loan/accept_loan_request.py b/loanpy/src/loan/accept_loan_request.py index fd39bd5ff83cff3ffa67a9d29c3c07f91125a961..4d79edc177ff342f4b12669d42da1620ab92fdac 100755 --- a/loanpy/src/loan/accept_loan_request.py +++ b/loanpy/src/loan/accept_loan_request.py @@ -23,17 +23,15 @@ Accept a loan request. from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success, get_timestamp -from .box_loan import (main, get_loan, F_LOAN, assert_loan_state, +from .box_loan import (main, get_loan, F_LOAN, LOAN_ACCEPTED, S_LOAN_ACCEPTED, S_LOAN_REQUESTED, get_borrower_names, set_property) def _accept_loan_request(loan): """Update a loan Record: add the `accepted` Property.""" - assert_loan_state(loan, S_LOAN_REQUESTED) - # This changes the state from "loan_requested" to "loan_accepted". - set_property(loan, LOAN_ACCEPTED, get_timestamp()) + update_loan_state(loan, LOAN_ACCEPTED) db.Container().extend([loan]).update() diff --git a/loanpy/src/loan/accept_return_request.py b/loanpy/src/loan/accept_return_request.py index ba91d876787a0d2124f48702f2b646f88159f3b0..7a647e09ee728b7e2c4c0b83e88d12c95baddf72 100755 --- a/loanpy/src/loan/accept_return_request.py +++ b/loanpy/src/loan/accept_return_request.py @@ -25,17 +25,15 @@ from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success, get_timestamp from .box_loan import (BOX_BORROWED, CONTENT, main, get_loan, set_property, - F_LOAN, assert_loan_state, RETURN_ACCEPTED, + F_LOAN, RETURN_ACCEPTED, RETURNLOCATION, S_RETURN_ACCEPTED, S_RETURN_REQUESTED, get_borrower_names, set_location_of_borrowed_items) def _accept_return_request(loan): """Update a loan Record and add the `accepted` Property.""" - assert_loan_state(loan, S_RETURN_REQUESTED) - # This changes the state from "return_requested" to "return_accepted". - set_property(loan, RETURN_ACCEPTED, get_timestamp()) + update_loan_state(loan, RETURN_ACCEPTED) items = set_location_of_borrowed_items(loan, RETURNLOCATION) if loan.get_property(CONTENT) is not None and loan.get_property(CONTENT).value: diff --git a/loanpy/src/loan/box_loan.py b/loanpy/src/loan/box_loan.py index 08bcac6ab299cd235e9086e228ba02ed30e17e4a..bf2a362d5f5414f9e10707da1b798f4e11451354 100644 --- a/loanpy/src/loan/box_loan.py +++ b/loanpy/src/loan/box_loan.py @@ -20,7 +20,7 @@ Creates a loan request using information provided by a web formular. """ -from __future__ import absolute_import +from __future__ import absolute_import, annotations import logging import sys @@ -29,7 +29,7 @@ from datetime import datetime import caosdb as db from caosadvancedtools.serverside.helper import (DataModelError, get_data, - init_data_model, + init_data_model,get_timestamp, parse_arguments, print_error, print_info, print_warning, send_mail) @@ -144,9 +144,9 @@ class StateError(RuntimeError): _expected = expected[0] if len(expected) == 1 else ",".join( expected[:-1]) + ", or" + expected[-1] super().__init__( - "This transition of the loan state is not possible. The expected " + "This transition of the loan state is not possible. The allowed target " "state is {one}'{expected}' " - "while the actual state is '{actual}'.".format(one=one, + "while the actual target state is '{actual}'.".format(one=one, actual=actual, expected=_expected)) @@ -212,6 +212,39 @@ def get_loan(loan_id): return get_record_by_id(LOAN.name, loan_id) +def update_loan_state(loan, new_state): + old_state = get_loan_state(loan) + if old_state==S_LOAN_REQUESTED: + if new_state==S_LOAN_ACCEPTED: + loan.add_property(LOAN_ACCEPTED, get_timestamp()) + else: + raise StateError(new_state, [S_LOAN_ACCEPTED]) + elif old_state==S_LOAN_ACCEPTED: + if new_state==S_LENT: + loan.add_property(LENT, get_timestamp()) + else: + raise StateError(new_state, [S_LENT]) + elif old_state==S_LENT: + if new_state==S_RETURN_REQUESTED: + loan.add_property(RETURN_REQUESTED, get_timestamp()) + else: + raise StateError(new_state, [S_RETURN_REQUESTED]) + elif old_state==S_RETURN_REQUESTED: + if new_state==S_RETURN_ACCEPTED: + loan.add_property(RETURN_ACCEPTED, get_timestamp()) + elif new_state==S_LENT: + loan.remove_property(RETURN_REQUESTED) + else: + raise StateError(new_state, [S_RETURN_ACCEPTED, S_LENT]) + elif old_state==S_RETURN_ACCEPTED: + if new_state==S_RETURNED: + loan.add_property(RETURNED, get_timestamp()) + else: + raise StateError(new_state, [S_RETURNED]) + else: + raise RuntimeError(f"Old state {old_state} is unknown.") + + def get_loan_state(loan): if loan.get_parent(LOAN) is None: raise MissingParentError(loan, LOAN) diff --git a/loanpy/src/loan/confirm_loan.py b/loanpy/src/loan/confirm_loan.py index dd0ac89e7028f94fbd7509b91b7b803b662a19b1..e21496f599205e1115f5543348cdf7e40c2cb484 100755 --- a/loanpy/src/loan/confirm_loan.py +++ b/loanpy/src/loan/confirm_loan.py @@ -26,7 +26,7 @@ import caosdb as db from caosadvancedtools.serverside.helper import get_timestamp, print_success from .box_loan import (BOX, BOX_BORROWED, DESTINATION, F_LOAN, LENT, S_LENT, - S_LOAN_ACCEPTED, assert_loan_state, get_borrower_names, + S_LOAN_ACCEPTED, get_borrower_names, get_loan, main, set_location_of_borrowed_items, set_property) @@ -50,18 +50,14 @@ def set_loan_location(loan): def _confirm_loan(data): """Update a loan Record and add the `lent` Property.""" loan = get_loan(data[F_LOAN]) - assert_loan_state(loan, S_LOAN_ACCEPTED) - # This changes the state from "loan_accepted" to "lent". - set_property(loan, LENT, get_timestamp()) + update_loan_state(loan, S_LENT) + _set_lent_box(loan) # updates the box location set_loan_location(loan) - # To be sure that it worked: - assert_loan_state(loan, S_LENT) - db.Container().extend([ loan ]).update() diff --git a/loanpy/src/loan/manual_return.py b/loanpy/src/loan/manual_return.py index 02003259e05e96293f3d614c2b2ad7b682327edf..0bb6bce242b102d4ad28f719b323b2935304d387 100755 --- a/loanpy/src/loan/manual_return.py +++ b/loanpy/src/loan/manual_return.py @@ -27,7 +27,7 @@ from caosadvancedtools.serverside.helper import get_timestamp, print_success from .box_loan import (BOX, BOX_BORROWED, BOX_RETURNED, CONTENT, F_LOAN, RETURNED, RETURNLOCATION, S_RETURN_ACCEPTED, S_RETURNED, - assert_loan_state, get_borrower_names, get_loan, main, + get_borrower_names, get_loan, main, set_location_of_borrowed_items, set_property) @@ -60,21 +60,15 @@ def set_content(loan): def _manual_return(data): """Update a loan Record and add the `returned` Property.""" loan = get_loan(data[F_LOAN]) - assert_loan_state(loan, S_RETURN_ACCEPTED) # This changes the state from "return_accepted" to "returned". - # TODO why twice???? - set_property(loan, RETURNED, get_timestamp()) - set_property(loan, RETURNED, get_timestamp()) + update_loan_state(loan, S_RETURNED) # updates the box location and content # *currently this is not wanted* # set_return_location(loan) # set_content(loan) - # To be sure that it worked: - assert_loan_state(loan, S_RETURNED) - # add returned box _set_returned_box(loan) diff --git a/loanpy/src/loan/reject_return_request.py b/loanpy/src/loan/reject_return_request.py index 58d2349055bf0870de257b6a1a250e18da24a65a..0c068234d620645ae52150cf40e1d62fb4ed0120 100755 --- a/loanpy/src/loan/reject_return_request.py +++ b/loanpy/src/loan/reject_return_request.py @@ -23,27 +23,18 @@ Reject a return request. from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success -from .box_loan import (main, get_loan, F_LOAN, assert_loan_state, +from .box_loan import (main, get_loan, F_LOAN, S_RETURN_REQUESTED, RETURN_REQUESTED, S_LENT, get_borrower_names) -def _reject_return_request(data): +def _reject_return_request(loan): """Update a `Loan` Record and remove the `returnRequested` Property.""" - loan = get_loan(data[F_LOAN]) - assert_loan_state(loan, S_RETURN_REQUESTED) # This changes the state from "return_requested" back to "lent". - loan.remove_property(RETURN_REQUESTED) - - # To be sure that it worked: - assert_loan_state(loan, S_LENT) + update_loan_state(loan, S_LENT) - db.Container().extend([ - loan - ]).update() - - return loan + loan.update() def reject_return_request(data): @@ -51,7 +42,8 @@ def reject_return_request(data): I.e. update the `Loan` Record and remove the `returnRequested` Property. """ - loan = _reject_return_request(data) + loan = get_loan(data[F_LOAN]) + _reject_return_request(loan) fn, ln = get_borrower_names(loan) print_success('You rejected return request by {fn} {ln}. See ' diff --git a/loanpy/src/loan/request_return.py b/loanpy/src/loan/request_return.py index 011f85aa206395a7c9a844a56e3184ca5157b081..823e0ce9e49e3844c4829d9c27875e6bef35b67d 100755 --- a/loanpy/src/loan/request_return.py +++ b/loanpy/src/loan/request_return.py @@ -33,7 +33,7 @@ from .box_loan import (BORROWER, COMMENT, CONTENT, EXPECTED_RETURN, F_COMMENT, F_FIRST_NAME, F_LAST_NAME, F_LOAN, FIRST_NAME, LAST_NAME, RETURN_REQUESTED, RETURNLOCATION, S_LENT, assert_date_in_future, assert_key_in_data, - assert_loan_state, get_loan, insert_or_update_person, main, + get_loan, insert_or_update_person, main, send_return_request_mail, set_property) @@ -53,10 +53,9 @@ def _check_data(data): def _issue_return_request(data): _check_data(data) loan = get_loan(data[F_LOAN]) - assert_loan_state(loan, S_LENT) + update_loan_state(loan, S_RETURN_REQUESTED) returner = insert_or_update_person(data[F_FIRST_NAME], data[F_LAST_NAME], data[F_EMAIL]) - loan.add_property(RETURN_REQUESTED, get_timestamp()) set_property(loan, BORROWER, returner.id) set_property(loan, EXPECTED_RETURN, data[F_EXPECTED_RETURN_DATE]) diff --git a/loanpy/unittests/test_box_loan.py b/loanpy/unittests/test_box_loan.py index 77906a04891c4c7da901d8863f9d9f814c7ac829..ebeccb7ac904a6e4803f2e6fb299a62d303bda10 100644 --- a/loanpy/unittests/test_box_loan.py +++ b/loanpy/unittests/test_box_loan.py @@ -20,10 +20,8 @@ from os.path import abspath, dirname, join from pytest import raises from linkahead import get_connection, configure_connection from linkahead.connection.mockup import (MockUpServerConnection, MockUpResponse) -from loan.box_loan import (_caller, create_person, - EMAIL, FIRST_NAME, LAST_NAME, PERSON, - assert_date_in_future, DataError, EmailPatternError, - assert_email_pattern) +from loan.box_loan import * +from loan.box_loan import _caller from utils import get_form_data_example @@ -60,3 +58,30 @@ def test_assert_date_in_future(): assert assert_date_in_future("2050-01-01") is None with raises(DataError): assert_date_in_future("1971-01-01") + + +def test_loan_states(): + loan = db.Record() + with raises(MissingParentError): + assert_loan_state(loan, S_LOAN_REQUESTED) + loan.add_parent(LOAN) + assert_loan_state(loan, None) + loan.add_property(LOAN_REQUESTED, 'a') + update_loan_state(loan, S_LOAN_ACCEPTED) + assert_loan_state(loan, S_LOAN_ACCEPTED) + update_loan_state(loan, S_LENT) + assert_loan_state(loan, S_LENT) + with raises(StateError): + update_loan_state(loan, S_LOAN_REQUESTED) + update_loan_state(loan, S_RETURN_REQUESTED) + assert_loan_state(loan, S_RETURN_REQUESTED) + update_loan_state(loan, S_LENT) + assert_loan_state(loan, S_LENT) + update_loan_state(loan, S_RETURN_REQUESTED) + assert_loan_state(loan, S_RETURN_REQUESTED) + update_loan_state(loan, S_RETURN_ACCEPTED) + assert_loan_state(loan, S_RETURN_ACCEPTED) + with raises(StateError): + update_loan_state(loan, S_LOAN_REQUESTED) + update_loan_state(loan, S_RETURNED) + assert_loan_state(loan, S_RETURNED)