diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index 630c6fd07..a2cbacb06 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -98,6 +98,7 @@ class EFTShortnameSummarySchema: last_payment_received_date: datetime credits_remaining: Decimal linked_accounts_count: int + refund_status: str @classmethod def from_row(cls, row: EFTShortnames): @@ -109,5 +110,6 @@ def from_row(cls, row: EFTShortnames): short_name=row.short_name, last_payment_received_date=getattr(row, 'last_payment_received_date', None), credits_remaining=getattr(row, 'credits_remaining', None), - linked_accounts_count=getattr(row, 'linked_accounts_count', None) + linked_accounts_count=getattr(row, 'linked_accounts_count', None), + refund_status=getattr(row, 'refund_status', None) ) diff --git a/pay-api/src/pay_api/services/eft_short_name_summaries.py b/pay-api/src/pay_api/services/eft_short_name_summaries.py index 9294e72f8..1c824035b 100644 --- a/pay-api/src/pay_api/services/eft_short_name_summaries.py +++ b/pay-api/src/pay_api/services/eft_short_name_summaries.py @@ -23,8 +23,9 @@ from pay_api.models import EFTShortnameSummarySchema as EFTSummarySchema from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import db +from pay_api.models.eft_refund import EFTRefund as EFTRefundModel from pay_api.services.eft_short_names import EFTShortnamesSearch -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameRefundStatus, EFTShortnameStatus from pay_api.utils.util import unstructure_schema_items @@ -76,6 +77,13 @@ def get_linked_count_query(): EFTShortnameStatus.LINKED.value])) .group_by(EFTShortnameLinksModel.eft_short_name_id)) + @staticmethod + def get_shortname_refund_query(): + """Query for EFT shortname refund.""" + return (db.session.query(EFTRefundModel.short_name_id, EFTRefundModel.status) + .filter(EFTRefundModel.status.in_([EFTShortnameRefundStatus.PENDING_REFUND.value])) + .distinct(EFTRefundModel.short_name_id)) + @staticmethod def get_remaining_credit_query(): """Query for EFT remaining credit amount.""" @@ -102,13 +110,15 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch): linked_account_subquery = cls.get_linked_count_query().subquery() credit_remaining_subquery = cls.get_remaining_credit_query().subquery() last_payment_subquery = cls.get_last_payment_received_query().subquery() + refund_shortname_subquery = cls.get_shortname_refund_query().subquery() query = (db.session.query( EFTShortnameModel.id, EFTShortnameModel.short_name, func.coalesce(linked_account_subquery.c.count, 0).label('linked_accounts_count'), func.coalesce(credit_remaining_subquery.c.total, 0).label('credits_remaining'), - last_payment_subquery.c.deposit_date.label('last_payment_received_date') + last_payment_subquery.c.deposit_date.label('last_payment_received_date'), + refund_shortname_subquery.c.status.label('refund_status') ).outerjoin( linked_account_subquery, linked_account_subquery.c.eft_short_name_id == EFTShortnameModel.id @@ -118,7 +128,10 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch): ).outerjoin( last_payment_subquery, and_(last_payment_subquery.c.short_name_id == EFTShortnameModel.id, last_payment_subquery.c.rn == 1) - )) + )).outerjoin( + refund_shortname_subquery, + refund_shortname_subquery.c.short_name_id == EFTShortnameModel.id + ) query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 5d54da353..3f0700edd 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -357,6 +357,12 @@ class EFTShortnameStatus(Enum): PENDING = 'PENDING' +class EFTShortnameRefundStatus(Enum): + """EFT Short name refund statuses.""" + + PENDING_REFUND = 'PENDING_REFUND' + + class EFTPaymentActions(Enum): """EFT Short name payment actions.""" diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index c1676cf50..bbae93de1 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -28,10 +28,11 @@ from pay_api.models import EFTShortnames as EFTShortnamesModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models.eft_refund import EFTRefund as EFTRefundModel from pay_api.services.eft_service import EftService from pay_api.utils.enums import ( - EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod, Role, - StatementFrequency) + EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameRefundStatus, EFTShortnameStatus, + InvoiceStatus, PaymentMethod, Role, StatementFrequency) from pay_api.utils.errors import Error from tests.utilities.base_test import ( factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, @@ -274,7 +275,8 @@ def assert_short_name_summary(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel, expected_credits_remaining: Decimal, - expected_linked_accounts_count: int): + expected_linked_accounts_count: int, + shortname_refund: EFTRefundModel = None): """Assert short name summary result.""" date_format = '%Y-%m-%dT%H:%M:%S' assert result_dict['id'] == short_name.id @@ -282,6 +284,7 @@ def assert_short_name_summary(result_dict: dict, assert result_dict['creditsRemaining'] == expected_credits_remaining assert result_dict['linkedAccountsCount'] == expected_linked_accounts_count assert datetime.strptime(result_dict['lastPaymentReceivedDate'], date_format) == transaction.deposit_date + assert result_dict['refundStatus'] == (shortname_refund.status if shortname_refund is not None else None) def test_eft_short_name_summaries(session, client, jwt, app): @@ -304,7 +307,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): name='ABC-123', branch_name='123').save() - short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_summary_search_data() + short_name_1, s1_transaction1, short_name_2, s2_transaction1, s1_refund = create_eft_summary_search_data() # Assert short name search brings back both short names rv = client.get('/api/v1/eft-shortnames/summaries?shortName=SHORT', headers=headers) @@ -319,9 +322,9 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 2 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) assert_short_name_summary(result_dict['items'][1], - short_name_2, s2_transaction1, 302.5, 1) + short_name_2, s2_transaction1, 302.5, 1, ) # Assert short name search brings back first short name rv = client.get('/api/v1/eft-shortnames/summaries?shortName=name1', headers=headers) @@ -336,7 +339,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) # Assert search linked accounts count rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=0', headers=headers) @@ -351,7 +354,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=1', headers=headers) assert rv.status_code == 200 @@ -411,7 +414,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) # Assert search query by no state will return all records rv = client.get('/api/v1/eft-shortnames/summaries', headers=headers) @@ -426,7 +429,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 2 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) assert_short_name_summary(result_dict['items'][1], short_name_2, s2_transaction1, 302.5, 1) @@ -443,7 +446,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert_short_name_summary(result_dict['items'][0], - short_name_1, s1_transaction1, 204.0, 0) + short_name_1, s1_transaction1, 204.0, 0, s1_refund) # Assert search pagination - page 2 works rv = client.get('/api/v1/eft-shortnames/summaries?page=2&limit=1', headers=headers) @@ -539,7 +542,16 @@ def create_eft_summary_search_data(): remaining_amount=s2_transaction1.deposit_amount_cents / 100 ).save() - return short_name_1, s1_transaction1, short_name_2, s2_transaction1 + s1_refund = EFTRefundModel( + short_name_id=short_name_1.id, + refund_amount=100.00, + cas_supplier_number='123', + refund_email='test@example.com', + comment='Test comment', + status=EFTShortnameRefundStatus.PENDING_REFUND.value + ).save() + + return short_name_1, s1_transaction1, short_name_2, s2_transaction1, s1_refund def create_eft_search_data():