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

24871 - Add route return valid payment method for product #1866

Merged
merged 4 commits into from
Dec 24, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""add payment_methods column to corp_types table, and insert payment methods based on product

Revision ID: 695a899f8a25
Revises: 4f3a44eeade8
Create Date: 2024-12-18 11:32:07.538729

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
# Note you may see foreign keys with distribution_codes_history
# For disbursement_distribution_code_id, service_fee_distribution_code_id
# Please ignore those lines and don't include in migration.

revision = '695a899f8a25'
down_revision = '4f3a44eeade8'
branch_labels = None
depends_on = None


def upgrade():
op.add_column(
'corp_types',
sa.Column('payment_methods', sa.ARRAY(sa.String()), nullable=True)
)

op.execute("""
UPDATE corp_types
SET payment_methods =
CASE product
WHEN 'BUSINESS' THEN ARRAY['PAD', 'DIRECT_PAY', 'EFT', 'EJV', 'ONLINE_BANKING', 'DRAWDOWN', 'INTERNAL']
WHEN 'NRO' THEN ARRAY['DIRECT_PAY','PAD', 'DRAWDOWN', 'INTERNAL']
WHEN 'RPPR' THEN ARRAY['PAD', 'DRAWDOWN', 'INTERNAL']
WHEN 'VS' THEN ARRAY['DIRECT_PAY','PAD', 'DRAWDOWN', 'EFT', 'EJV', 'INTERNAL']
WHEN 'RPT' THEN ARRAY['PAD', 'DRAWDOWN', 'EFT', 'EJV', 'INTERNAL']
WHEN 'BUSINESS_SEARCH' THEN ARRAY['PAD', 'DIRECT_PAY', 'DRAWDOWN', 'EJV', 'EFT', 'INTERNAL']
WHEN 'CSO' THEN ARRAY['PAD', 'DRAWDOWN', 'EJV', 'INTERNAL']
WHEN 'ESRA' THEN ARRAY['PAD', 'DRAWDOWN', 'EJV', 'INTERNAL']
WHEN 'PPR' THEN ARRAY['PAD', 'DIRECT_PAY', 'DRAWDOWN', 'EFT', 'EJV', 'INTERNAL']
WHEN 'MHR' THEN ARRAY['PAD', 'DIRECT_PAY', 'DRAWDOWN', 'EFT', 'EJV', 'INTERNAL']
ELSE ARRAY['INTERNAL']
END
WHERE product IS NOT NULL
""")


def downgrade():
op.drop_column('corp_types', 'payment_methods')
2 changes: 2 additions & 0 deletions pay-api/src/pay_api/models/corp_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CorpType(db.Model, CodeTable):
"description",
"has_partner_disbursements",
"is_online_banking_allowed",
"payment_methods",
"product",
]
}
Expand All @@ -61,6 +62,7 @@ class CorpType(db.Model, CodeTable):
has_partner_disbursements = db.Column(Boolean(), default=False)
batch_type = db.Column(db.String(2), nullable=True)
product = db.Column(db.String(20), nullable=True)
payment_methods = db.Column(db.ARRAY(db.String()), nullable=True)

def save(self):
"""Save corp type."""
Expand Down
8 changes: 8 additions & 0 deletions pay-api/src/pay_api/resources/v1/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,11 @@ def get_codes_by_type(code_type):
def get_code(code_type, code):
"""Return all codes based on code_type."""
return CodeService.find_code_value_by_type_and_code(code_type, code), HTTPStatus.OK


@bp.route("/valid_payment_methods", methods=["GET", "OPTIONS"])
@bp.route("/valid_payment_methods/<string:product_code>", methods=["GET", "OPTIONS"])
@cross_origin(origins="*", methods=["GET"])
def get_valid_payment_methods(product_code=None):
"""Return all valid payment methods based on product code."""
return CodeService.find_valid_payment_methods_by_product_code(product_code), HTTPStatus.OK
19 changes: 19 additions & 0 deletions pay-api/src/pay_api/services/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,22 @@ def find_code_value_by_type_and_code(cls, code_type: str, code: str):
code_response = schema.dump(codes_model, many=False)
current_app.logger.debug(">find_code_value_by_type_and_code")
return code_response

@classmethod
def find_valid_payment_methods_by_product_code(cls, product_code: str | None = None) -> dict:
"""Find payment methods for a product."""
if not product_code:
corp_types = (
CorpType.query.with_entities(CorpType.product, CorpType.payment_methods)
.filter(CorpType.product.isnot(None)) # Exclude None at the query level
.distinct()
.all()
)
return dict(corp_types)

corp_type = (
CorpType.query.with_entities(CorpType.product, CorpType.payment_methods)
.filter_by(product=product_code)
.first()
)
return {corp_type.product: corp_type.payment_methods} if corp_type else {}
15 changes: 15 additions & 0 deletions pay-api/tests/unit/api/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,18 @@ def test_find_codes(session, client, jwt, app, code: Code):
"""Assert that the endpoint returns 200."""
rv = client.get(f"/api/v1/codes/{code.value}", headers={})
assert rv.status_code == 200


def test_get_valid_payment_methods(session, client, jwt, app):
"""Assert that the valid payment methods endpoint works."""
rv = client.get("/api/v1/codes/valid_payment_methods", headers={})
assert rv.status_code == 200
assert isinstance(rv.json, dict)

rv = client.get("/api/v1/codes/valid_payment_methods/VS", headers={})
assert rv.status_code == 200
assert isinstance(rv.json, dict)

rv = client.get("/api/v1/codes/valid_payment_methods/INVALID", headers={})
assert rv.status_code == 200
assert rv.json == {}
25 changes: 23 additions & 2 deletions pay-api/tests/unit/models/test_corp_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@
from pay_api.models import CorpType


def factory_corp_type(corp_type_code: str, corp_description: str):
def factory_corp_type(corp_type_code: str, corp_description: str, product: str = None, payment_methods: list = None):
"""Return a valid Corp Type object."""
return CorpType(code=corp_type_code, description=corp_description)
corp_type = CorpType(code=corp_type_code, description=corp_description)
if product:
corp_type.product = product
if payment_methods:
corp_type.payment_methods = payment_methods
return corp_type


def test_corp_type(session):
Expand Down Expand Up @@ -54,3 +59,19 @@ def test_corp_type_by_invalid_code(session):

b = CorpType.find_by_code("AB")
assert b is None


def test_payment_methods(session):
"""Assert that payment methods are stored and retrieved correctly."""
business_corp = factory_corp_type(
"XX",
"Business",
product="BUSINESS",
payment_methods=['PAD', 'DIRECT_PAY', 'ONLINE_BANKING', 'DRAWDOWN']
)
session.add(business_corp)
session.commit()

retrieved_corp = CorpType.find_by_code("XX")
assert retrieved_corp is not None
assert retrieved_corp.payment_methods == ['PAD', 'DIRECT_PAY', 'ONLINE_BANKING', 'DRAWDOWN']
15 changes: 15 additions & 0 deletions pay-api/tests/unit/services/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,18 @@ def test_find_payment_types_code_values(session):
codes = CodeService.find_code_values_by_type(Code.INVOICE_STATUS.value)
assert codes is not None
assert len(codes) > 0


def test_find_valid_payment_methods_by_product_code(session):
"""Assert that valid payment methods are returned for products."""
payment_methods = CodeService.find_valid_payment_methods_by_product_code()
assert payment_methods is not None
assert isinstance(payment_methods, dict)

business_payment_methods = CodeService.find_valid_payment_methods_by_product_code('BUSINESS')
assert business_payment_methods is not None
assert 'BUSINESS' in business_payment_methods
assert isinstance(business_payment_methods['BUSINESS'], list)

invalid_payment_methods = CodeService.find_valid_payment_methods_by_product_code('INVALID')
assert invalid_payment_methods == {}
Loading