From 6dc698f19da3930c854be98cef20412aeba7f7ce Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Tue, 24 Dec 2024 09:39:27 -0800 Subject: [PATCH] 24871 - Add route return valid payment method for product (#1866) --- ...f8a25_add_payment_methods_to_corp_types.py | 50 +++++++++++++++++++ pay-api/src/pay_api/models/corp_type.py | 2 + pay-api/src/pay_api/resources/v1/code.py | 8 +++ pay-api/src/pay_api/services/code.py | 19 +++++++ pay-api/tests/unit/api/test_code.py | 15 ++++++ pay-api/tests/unit/models/test_corp_type.py | 25 +++++++++- pay-api/tests/unit/services/test_code.py | 15 ++++++ 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 pay-api/migrations/versions/2024_12_18_695a899f8a25_add_payment_methods_to_corp_types.py diff --git a/pay-api/migrations/versions/2024_12_18_695a899f8a25_add_payment_methods_to_corp_types.py b/pay-api/migrations/versions/2024_12_18_695a899f8a25_add_payment_methods_to_corp_types.py new file mode 100644 index 000000000..b52c007dd --- /dev/null +++ b/pay-api/migrations/versions/2024_12_18_695a899f8a25_add_payment_methods_to_corp_types.py @@ -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') diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index eb2cfe5af..516b0af2e 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -47,6 +47,7 @@ class CorpType(db.Model, CodeTable): "description", "has_partner_disbursements", "is_online_banking_allowed", + "payment_methods", "product", ] } @@ -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.""" diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index 2f3be890f..052a5f892 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -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/", 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 diff --git a/pay-api/src/pay_api/services/code.py b/pay-api/src/pay_api/services/code.py index ef25a5489..d6b3e1058 100644 --- a/pay-api/src/pay_api/services/code.py +++ b/pay-api/src/pay_api/services/code.py @@ -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 {} diff --git a/pay-api/tests/unit/api/test_code.py b/pay-api/tests/unit/api/test_code.py index 39c0af4b4..d494a4a49 100755 --- a/pay-api/tests/unit/api/test_code.py +++ b/pay-api/tests/unit/api/test_code.py @@ -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 == {} diff --git a/pay-api/tests/unit/models/test_corp_type.py b/pay-api/tests/unit/models/test_corp_type.py index 2b7785c80..c872751ba 100644 --- a/pay-api/tests/unit/models/test_corp_type.py +++ b/pay-api/tests/unit/models/test_corp_type.py @@ -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): @@ -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'] diff --git a/pay-api/tests/unit/services/test_code.py b/pay-api/tests/unit/services/test_code.py index 55c493f8c..4f198cf17 100644 --- a/pay-api/tests/unit/services/test_code.py +++ b/pay-api/tests/unit/services/test_code.py @@ -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 == {}