Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

19334 - Adding Find EFT Accounts Endpoint #1392

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pay-api/src/pay_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from .non_sufficient_funds import NonSufficientFundsModel, NonSufficientFundsSchema
from .notification_status_code import NotificationStatusCode, NotificationStatusCodeSchema
from .payment import Payment, PaymentSchema
from .payment_account import PaymentAccount, PaymentAccountSchema # noqa: I001
from .payment_account import PaymentAccount, PaymentAccountSchema, PaymentAccountSearchModel # noqa: I001
from .payment_line_item import PaymentLineItem, PaymentLineItemSchema
from .payment_method import PaymentMethod, PaymentMethodSchema
from .payment_status_code import PaymentStatusCode, PaymentStatusCodeSchema
Expand Down
2 changes: 2 additions & 0 deletions pay-api/src/pay_api/resources/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .bank_accounts import bp as bank_accounts_bp
from .code import bp as code_bp
from .distributions import bp as distributions_bp
from .eft_accounts import bp as eft_accounts_bp
from .eft_short_names import bp as eft_short_names_bp
from .fas import fas_refund_bp, fas_routing_slip_bp
from .fee import bp as fee_bp
Expand Down Expand Up @@ -58,6 +59,7 @@ def init_app(self, app):
self.app.register_blueprint(bank_accounts_bp)
self.app.register_blueprint(code_bp)
self.app.register_blueprint(distributions_bp)
self.app.register_blueprint(eft_accounts_bp)
self.app.register_blueprint(eft_short_names_bp)
self.app.register_blueprint(fas_refund_bp)
self.app.register_blueprint(fas_routing_slip_bp)
Expand Down
47 changes: 47 additions & 0 deletions pay-api/src/pay_api/resources/v1/eft_accounts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright © 2019 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Resource for Payment account."""
from http import HTTPStatus

from flask import Blueprint, current_app, request
from flask_cors import cross_origin

from pay_api.services.payment_account import EFTAccountsSearch
from pay_api.services.payment_account import PaymentAccount as PaymentAccountService
from pay_api.utils.auth import jwt as _jwt
from pay_api.utils.endpoints_enums import EndpointEnum
from pay_api.utils.enums import Role
from pay_api.utils.trace import tracing as _tracing


bp = Blueprint('EFT_ACCOUNTS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/eft-accounts')


@bp.route('', methods=['GET', 'OPTIONS'])
@cross_origin(origins='*', methods=['GET'])
@_tracing.trace()
@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.STAFF.value])
def get_eft_accounts():
"""Get EFT Payment Accounts."""
eft_accounts_search = EFTAccountsSearch(
request.args.get('state', None),
int(request.args.get('page', '1')),
int(request.args.get('limit', '10')))

current_app.logger.info('<get_eft_accounts')

response, status = PaymentAccountService.find_eft_accounts(eft_accounts_search), HTTPStatus.OK

current_app.logger.debug('>get_eft_accounts')
return response, status
50 changes: 47 additions & 3 deletions pay-api/src/pay_api/services/payment_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"""Service to manage Payment Account model related operations."""
from __future__ import annotations

from dataclasses import dataclass
from datetime import date, datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Dict, List, Optional, Tuple
Expand All @@ -28,9 +29,10 @@
from pay_api.models import CfsAccount as CfsAccountModel
from pay_api.models import EFTCredit as EFTCreditModel
from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel
from pay_api.models import EFTShortnames as EFTShortnamesModel
from pay_api.models import Invoice as InvoiceModel
from pay_api.models import PaymentAccount as PaymentAccountModel
from pay_api.models import PaymentAccountSchema
from pay_api.models import PaymentAccountSchema, PaymentAccountSearchModel
from pay_api.models import StatementRecipients as StatementRecipientModel
from pay_api.models import StatementSettings as StatementSettingsModel
from pay_api.models import db
Expand All @@ -40,9 +42,10 @@
from pay_api.services.queue_publisher import publish_response
from pay_api.services.statement import Statement
from pay_api.services.statement_settings import StatementSettings
from pay_api.utils.converter import Converter
from pay_api.utils.enums import (
AuthHeaderType, CfsAccountStatus, ContentType, InvoiceStatus, MessageType, PaymentMethod, PaymentSystem,
StatementFrequency)
AuthHeaderType, CfsAccountStatus, ContentType, EFTShortnameState, InvoiceStatus, MessageType, PaymentMethod,
PaymentSystem, StatementFrequency)
from pay_api.utils.errors import Error
from pay_api.utils.user_context import UserContext, user_context
from pay_api.utils.util import (
Expand All @@ -52,6 +55,15 @@
from .flags import flags


@dataclass
class EFTAccountsSearch:
"""Used for searching EFT accounts."""

state: Optional[str] = None
page: Optional[int] = 1
limit: Optional[int] = 10


class PaymentAccount(): # pylint: disable=too-many-instance-attributes, too-many-public-methods
"""Service to manage Payment Account model related operations."""

Expand Down Expand Up @@ -652,6 +664,38 @@ def find_by_id(cls, account_id: int):
account._dao = PaymentAccountModel.find_by_id(account_id) # pylint: disable=protected-access
return account

@classmethod
def find_eft_accounts(cls, eft_accounts_search: EFTAccountsSearch):
"""Find EFT accounts."""
query = db.session.query(PaymentAccountModel) \
.outerjoin(EFTShortnamesModel, PaymentAccountModel.auth_account_id == EFTShortnamesModel.auth_account_id) \
.filter(PaymentAccountModel.payment_method == PaymentMethod.EFT.value,
PaymentAccountModel.eft_enable.is_(True))

if eft_accounts_search.state == EFTShortnameState.UNLINKED.value:
query = query.filter(EFTShortnamesModel.auth_account_id.is_(None))
elif eft_accounts_search.state == EFTShortnameState.LINKED.value:
query = query.filter(EFTShortnamesModel.auth_account_id.isnot(None))

query = query.order_by(PaymentAccountModel.id)
pagination = query.paginate(per_page=eft_accounts_search.limit, page=eft_accounts_search.page)

total = pagination.total
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'total': pagination.total

eft_accounts_list = [PaymentAccountSearchModel.from_row(eft_account) for eft_account in pagination.items]

eft_accounts = pagination.items

eft_accounts_list = [PaymentAccountSearchModel.from_row(eft_account) for eft_account in eft_accounts]
converter = Converter()
eft_accounts_list = converter.unstructure(eft_accounts_list)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converter().unstructure(eft_accounts_list)


data = {
'total': total,
'page': eft_accounts_search.page,
'limit': eft_accounts_search.limit,
'items': eft_accounts_list
}

return data

@classmethod
def get_eft_credit_balance(cls, account_id: int) -> Decimal:
"""Calculate pay account eft balance by account id."""
Expand Down
83 changes: 78 additions & 5 deletions pay-api/tests/unit/services/test_payment_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@

from pay_api.exceptions import BusinessException
from pay_api.models import CfsAccount as CfsAccountModel
from pay_api.models import EFTFile as EFTFileModel
from pay_api.models import EFTCredit as EFTCreditModel
from pay_api.models import EFTFile as EFTFileModel
from pay_api.models import EFTShortnames as EFTShortnameModel
from pay_api.models import Invoice as InvoiceModel
from pay_api.models import StatementRecipients as StatementRecipientModel
from pay_api.models import StatementSettings as StatementSettingsModel
from pay_api.services.payment_account import EFTAccountsSearch
from pay_api.services.payment_account import PaymentAccount as PaymentAccountService
from pay_api.utils.enums import CfsAccountStatus, InvoiceStatus, PaymentMethod, StatementFrequency
from pay_api.utils.enums import CfsAccountStatus, EFTShortnameState, InvoiceStatus, PaymentMethod, StatementFrequency
from pay_api.utils.errors import Error
from pay_api.utils.util import get_outstanding_txns_from_date
from tests.utilities.base_test import (
factory_invoice, factory_payment_account, factory_premium_payment_account, get_auth_basic_user,
get_auth_premium_user, get_basic_account_payload, get_eft_enable_account_payload, get_pad_account_payload,
get_premium_account_payload, get_unlinked_pad_account_payload)
factory_eft_shortname, factory_invoice, factory_payment_account, factory_premium_payment_account,
get_auth_basic_user, get_auth_premium_user, get_basic_account_payload, get_eft_enable_account_payload,
get_pad_account_payload, get_premium_account_payload, get_unlinked_pad_account_payload)


def test_account_saved_from_new(session):
Expand Down Expand Up @@ -383,3 +384,75 @@ def test_eft_payment_method_settings(session, client, jwt, app, admin_users_mock

assert statement_settings is not None
assert statement_settings.frequency == StatementFrequency.MONTHLY.value


def test_find_eft_accounts(session, client, jwt, app, admin_users_mock):
"""Assert that the find EFT accounts is working as expected."""
page = 1
limit = 5
auth_account_id = '12345678'
payment_account = PaymentAccountService.create(get_premium_account_payload(
payment_method=PaymentMethod.EFT.value, account_id=auth_account_id))
PaymentAccountService.enable_eft(payment_account.auth_account_id)
eft_shortname = factory_eft_shortname(short_name='TESTSHORTNAME', auth_account_id=auth_account_id).save()

payment_account = PaymentAccountService.find_by_id(payment_account.id)
assert payment_account.payment_method == PaymentMethod.EFT.value
assert payment_account.eft_enable is True
assert payment_account.auth_account_id == auth_account_id

assert eft_shortname.auth_account_id == auth_account_id

eft_accounts = PaymentAccountService.find_eft_accounts(EFTAccountsSearch(
EFTShortnameState.UNLINKED.value, page, limit))
assert eft_accounts['limit'] == limit
assert eft_accounts['page'] == page
assert eft_accounts['total'] == 0

# Create 10 eft enabled accounts
for i in range(10):
payment_account = PaymentAccountService.create(get_premium_account_payload(
account_id=f'{123}{i}', payment_method=PaymentMethod.EFT.value))
PaymentAccountService.enable_eft(payment_account.auth_account_id)

payment_account = PaymentAccountService.find_by_id(payment_account.id)
assert payment_account.payment_method == PaymentMethod.EFT.value
assert payment_account.eft_enable is True

# Create 5 eft shortnames
for i in range(5):
eft_shortname = factory_eft_shortname(short_name=f'TESTSHORTNAME{i}', auth_account_id=f'{123}{i}').save()

eft_accounts = PaymentAccountService.find_eft_accounts(EFTAccountsSearch(
EFTShortnameState.UNLINKED.value, page, limit))
assert eft_accounts['limit'] == limit
assert eft_accounts['page'] == page
assert eft_accounts['total'] == 5

# Create 5 eft enabled accounts
for i in range(5):
payment_account = PaymentAccountService.create(get_premium_account_payload(
account_id=f'{1234}{i}', payment_method=PaymentMethod.EFT.value))
PaymentAccountService.enable_eft(payment_account.auth_account_id)

payment_account = PaymentAccountService.find_by_id(payment_account.id)
assert payment_account.payment_method == PaymentMethod.EFT.value
assert payment_account.eft_enable is True

# Create 5 eft disabled accounts
for i in range(5):
payment_account = PaymentAccountService.create(get_premium_account_payload(
account_id=f'{12345}{i}', payment_method=PaymentMethod.EFT.value))
payment_account = PaymentAccountService.find_by_id(payment_account.id)
assert payment_account.payment_method == PaymentMethod.EFT.value
assert payment_account.eft_enable is False

# Create 5 eft shortnames
for i in range(5):
eft_shortname = factory_eft_shortname(short_name=f'TESTSHORTNAME{i}', auth_account_id=f'{123}{i}').save()

eft_accounts = PaymentAccountService.find_eft_accounts(EFTAccountsSearch(
EFTShortnameState.UNLINKED.value, page, limit))
assert eft_accounts['limit'] == limit
assert eft_accounts['page'] == page
assert eft_accounts['total'] == 10
Loading