Skip to content

Commit

Permalink
19331 - EFT Shortname search (#1394)
Browse files Browse the repository at this point in the history
* 19331 - EFT Shortname search

- account_id search
- account_name search
- account_branch - initial setup (pending data updates)

* Update account_id to be partial match

* Fix linting

* lint fixes

* 19331 update logic for account branch

* Add supporting filter for a list of account ids for linking support
  • Loading branch information
ochiu authored Feb 1, 2024
1 parent 755014f commit 5942923
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 24 deletions.
7 changes: 3 additions & 4 deletions pay-api/src/pay_api/models/eft_short_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from .base_model import VersionedModel
from .db import db
from ..utils.util import cents_to_decimal, parse_account_name_and_branch
from ..utils.util import cents_to_decimal


class EFTShortnames(VersionedModel): # pylint: disable=too-many-instance-attributes
Expand Down Expand Up @@ -77,12 +77,11 @@ def from_row(cls, row: EFTShortnames):
https://www.attrs.org/en/stable/init.html
"""
account_name, account_branch = parse_account_name_and_branch(getattr(row, 'account_name', None))
return cls(id=row.id,
short_name=row.short_name,
account_id=row.auth_account_id,
account_name=account_name,
account_branch=account_branch,
account_name=getattr(row, 'account_name', None),
account_branch=getattr(row, 'account_branch', None),
created_on=row.created_on,
transaction_id=getattr(row, 'transaction_id', None),
transaction_date=getattr(row, 'transaction_date', None),
Expand Down
9 changes: 9 additions & 0 deletions pay-api/src/pay_api/resources/v1/eft_short_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,17 @@ def get_eft_shortnames():
deposit_amount = request.args.get('depositAmount', None)
deposit_date = request.args.get('depositDate', None)
short_name = request.args.get('shortName', None)
account_id = request.args.get('accountId', None)
account_id_list = request.args.get('accountIdList', None)
account_id_list = account_id_list.split(',') if account_id_list else None
account_name = request.args.get('accountName', None)
account_branch = request.args.get('accountBranch', None)

response, status = EFTShortnameService.search(EFTShortnamesSearch(
account_id=account_id,
account_id_list=account_id_list,
account_name=account_name,
account_branch=account_branch,
deposit_date=string_to_date(deposit_date),
deposit_amount=Decimal(deposit_amount) * Decimal(100) if deposit_amount else None,
short_name=short_name,
Expand Down
29 changes: 26 additions & 3 deletions pay-api/src/pay_api/services/eft_short_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from _decimal import Decimal
from flask import current_app
from sqlalchemy import func
from sqlalchemy import case, func

from pay_api.exceptions import BusinessException
from pay_api.factory.payment_system_factory import PaymentSystemFactory
Expand All @@ -38,9 +38,13 @@


@dataclass
class EFTShortnamesSearch:
class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes
"""Used for searching EFT short name records."""

account_id_list: Optional[List[str]] = None
account_id: Optional[str] = None
account_name: Optional[str] = None
account_branch: Optional[str] = None
transaction_date: Optional[date] = None
deposit_date: Optional[date] = None
deposit_amount: Optional[Decimal] = None
Expand Down Expand Up @@ -267,11 +271,21 @@ def get_ordered_transaction_query():
def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = False):
"""Query for short names based on search criteria."""
sub_query = None

# Case statement is to check for and remove the branch name from the name, so they can be filtered on separately
# The branch name was added to facilitate a better short name search experience and the existing
# name is preserved as it was with '-' concatenated with the branch name for reporting purposes
query = db.session.query(EFTShortnameModel.id,
EFTShortnameModel.short_name,
EFTShortnameModel.auth_account_id,
EFTShortnameModel.created_on,
PaymentAccountModel.name.label('account_name'))
case(
[(PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name),
func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '')
)],
else_=PaymentAccountModel.name
).label('account_name'),
PaymentAccountModel.branch_name.label('account_branch'))

# Join payment information if this is NOT the count query
if not is_count:
Expand All @@ -288,13 +302,22 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool =
query = query.filter_conditionally(search_criteria.transaction_date, sub_query.c.transaction_date)
query = query.filter_conditionally(search_criteria.deposit_date, sub_query.c.deposit_date)
query = query.filter_conditionally(search_criteria.deposit_amount, sub_query.c.deposit_amount_cents)
query = query.filter_conditionally(search_criteria.account_id,
PaymentAccountModel.auth_account_id, is_like=True)
query = query.filter_conditionally(search_criteria.account_name, PaymentAccountModel.name, is_like=True)
query = query.filter_conditionally(search_criteria.account_branch, PaymentAccountModel.branch_name,
is_like=True)

# Filter by short name state
if search_criteria.state == EFTShortnameState.UNLINKED.value:
query = query.filter(EFTShortnameModel.auth_account_id.is_(None))
elif search_criteria.state == EFTShortnameState.LINKED.value:
query = query.filter(EFTShortnameModel.auth_account_id.isnot(None))

# Filter by a list of auth account ids - full match
if search_criteria.account_id_list:
query = query.filter(EFTShortnameModel.auth_account_id.in_(search_criteria.account_id_list))

# Short name free text search
query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True)
query = query.order_by(sub_query.c.transaction_date) if sub_query is not None else query
Expand Down
15 changes: 0 additions & 15 deletions pay-api/src/pay_api/utils/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,18 +269,3 @@ def cents_to_decimal(amount: int):
return None

return amount / 100


def parse_account_name_and_branch(name: str):
"""Return account name and branch."""
if name is None:
return None, None

# Split on the last dash in the event the main name has a dash
split_name = name.rsplit('-', 1)

# There is no branch name
if len(split_name) == 1:
return split_name[0], None

return split_name[0], split_name[1]
81 changes: 80 additions & 1 deletion pay-api/tests/unit/api/test_eft_short_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def test_search_eft_short_names(session, client, jwt, app):
# create test data
payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value,
auth_account_id='1234',
name='ABC-123').save()
name='ABC-123',
branch_name='123').save()

eft_file: EFTFileModel = factory_eft_file()
short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save()
Expand Down Expand Up @@ -194,8 +195,40 @@ def test_search_eft_short_names(session, client, jwt, app):
assert result_dict['items'][0]['accountBranch'] == '123'
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Assert search account name
rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers)
assert rv.status_code == 200

result_dict = rv.json
assert result_dict is not None
assert result_dict['page'] == 1
assert result_dict['stateTotal'] == 1
assert result_dict['total'] == 1
assert result_dict['limit'] == 10
assert result_dict['items'] is not None
assert len(result_dict['items']) == 1
assert result_dict['items'][0]['accountName'] == 'ABC'
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Assert search account branch
rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountBranch=2', headers=headers)
assert rv.status_code == 200

result_dict = rv.json
assert result_dict is not None
assert result_dict['page'] == 1
assert result_dict['stateTotal'] == 1
assert result_dict['total'] == 1
assert result_dict['limit'] == 10
assert result_dict['items'] is not None
assert len(result_dict['items']) == 1
assert result_dict['items'][0]['accountName'] == 'ABC'
assert result_dict['items'][0]['accountBranch'] == '123'
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Update payment account to not have a branch name
payment_account.name = 'ABC'
payment_account.branch_name = None
payment_account.save()

# Assert search returns linked short names with payment account name that has no branch
Expand All @@ -215,6 +248,21 @@ def test_search_eft_short_names(session, client, jwt, app):
assert result_dict['items'][0]['accountBranch'] is None
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Assert search account name
rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers)
assert rv.status_code == 200

result_dict = rv.json
assert result_dict is not None
assert result_dict['page'] == 1
assert result_dict['stateTotal'] == 1
assert result_dict['total'] == 1
assert result_dict['limit'] == 10
assert result_dict['items'] is not None
assert len(result_dict['items']) == 1
assert result_dict['items'][0]['accountName'] == 'ABC'
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Assert search query by no state will return all records
rv = client.get('/api/v1/eft-shortnames', headers=headers)
assert rv.status_code == 200
Expand Down Expand Up @@ -329,6 +377,37 @@ def test_search_eft_short_names(session, client, jwt, app):
assert len(result_dict['items']) == 1
assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1)

# Assert search account id
rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1234', headers=headers)
assert rv.status_code == 200

result_dict = rv.json
assert result_dict is not None
assert result_dict['page'] == 1
assert result_dict['stateTotal'] == 1
assert result_dict['total'] == 1
assert result_dict['limit'] == 10
assert result_dict['items'] is not None
assert len(result_dict['items']) == 1
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)

# Assert search account id list
rv = client.get('/api/v1/eft-shortnames?accountIdList=1,1234', headers=headers)
assert rv.status_code == 200

result_dict = rv.json
assert result_dict is not None
assert result_dict['page'] == 1
assert result_dict['stateTotal'] == 2
assert result_dict['total'] == 1
assert result_dict['limit'] == 10
assert result_dict['items'] is not None
assert len(result_dict['items']) == 1
assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2'
assert result_dict['items'][0]['accountName'] == 'ABC'
assert result_dict['items'][0]['accountBranch'] is None
assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1)


def test_apply_eft_short_name_credits(session, client, jwt, app):
"""Assert that credits are applied to invoices when short name is mapped to an account."""
Expand Down
4 changes: 3 additions & 1 deletion pay-api/tests/utilities/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,16 @@ def factory_payment_account(payment_system_code: str = 'PAYBC', payment_method_c
bcol_user_id='test',
auth_account_id: str = '1234',
cfs_account_status: str = CfsAccountStatus.ACTIVE.value,
name=None):
name=None,
branch_name=None):
"""Return Factory."""
# Create a payment account
account = PaymentAccount(
auth_account_id=auth_account_id,
bcol_user_id=bcol_user_id,
bcol_account='TEST',
name=name,
branch_name=branch_name,
payment_method=payment_method_code,
pad_activation_date=datetime.now(),
eft_enable=False
Expand Down

0 comments on commit 5942923

Please sign in to comment.