Skip to content

Commit

Permalink
add fsp_auth_code to payments table (#3870)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarekBiczysko authored May 17, 2024
1 parent db95355 commit 72c6ffe
Show file tree
Hide file tree
Showing 24 changed files with 466 additions and 86 deletions.
1 change: 1 addition & 0 deletions backend/hct_mis_api/apps/account/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def _generate_next_value_(name: str, start: int, count: int, last_values: List[A
PM_MARK_PAYMENT_AS_FAILED = auto()
PM_EXPORT_PDF_SUMMARY = auto()
PM_SEND_TO_PAYMENT_GATEWAY = auto()
PM_VIEW_FSP_AUTH_CODE = auto()

# Payment Module Admin
PM_ADMIN_FINANCIAL_SERVICE_PROVIDER_UPDATE = auto()
Expand Down
18 changes: 18 additions & 0 deletions backend/hct_mis_api/apps/payment/migrations/0129_migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.25 on 2024-05-15 11:29

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('payment', '0128_migration'),
]

operations = [
migrations.AddField(
model_name='payment',
name='fsp_auth_code',
field=models.CharField(blank=True, help_text='FSP Auth Code', max_length=128, null=True),
),
]
1 change: 1 addition & 0 deletions backend/hct_mis_api/apps/payment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1709,6 +1709,7 @@ class Payment(SoftDeletableModel, GenericPayment, UnicefIdentifiedModel, AdminUr
additional_document_number = models.CharField(
max_length=128, blank=True, null=True, help_text="Use this field for reconciliation data"
)
fsp_auth_code = models.CharField(max_length=128, blank=True, null=True, help_text="FSP Auth Code")

@property
def full_name(self) -> str:
Expand Down
12 changes: 12 additions & 0 deletions backend/hct_mis_api/apps/payment/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ class PaymentNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectType):
snapshot_collector_bank_name = graphene.String(description="Get from Household Snapshot")
snapshot_collector_bank_account_number = graphene.String(description="Get from Household Snapshot")
snapshot_collector_debit_card_number = graphene.String(description="Get from Household Snapshot")
fsp_auth_code = graphene.String()

class Meta:
model = Payment
Expand Down Expand Up @@ -450,6 +451,17 @@ def _parse_pp_conflict_data(cls, conflicts_data: List) -> List[Any]:
"""parse list of conflicted payment plans data from Payment model json annotations"""
return [json.loads(conflict) for conflict in conflicts_data]

def resolve_fsp_auth_code(self, info: Any) -> str:
user = info.context.user

if not user.has_permission(
Permissions.PM_VIEW_FSP_AUTH_CODE.value,
self.business_area,
self.program_id,
):
return ""
return self.fsp_auth_code or "" # type: ignore


class DeliveryMechanismNode(DjangoObjectType):
name = graphene.String()
Expand Down
28 changes: 17 additions & 11 deletions backend/hct_mis_api/apps/payment/services/payment_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ class Endpoints:
GET_FSPS = "fsp/"
GET_PAYMENT_RECORDS = "payment_records/"

def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None) -> None:
self.api_key = api_key or os.getenv("PAYMENT_GATEWAY_API_KEY")
self.api_url = api_url or os.getenv("PAYMENT_GATEWAY_API_URL")
def __init__(self) -> None:
self.api_key = os.getenv("PAYMENT_GATEWAY_API_KEY")
self.api_url = os.getenv("PAYMENT_GATEWAY_API_URL")

if not self.api_key or not self.api_url:
raise self.PaymentGatewayMissingAPICredentialsException("Missing Payment Gateway API Key/URL")
Expand Down Expand Up @@ -386,7 +386,8 @@ def update_payment(

_payment.status = matching_pg_payment.hope_status
_payment.status_date = now()
update_fields = ["status", "status_date"]
_payment.fsp_auth_code = matching_pg_payment.auth_code
update_fields = ["status", "status_date", "fsp_auth_code"]

if _payment.status not in Payment.ALLOW_CREATE_VERIFICATION and matching_pg_payment.message:
_payment.reason_for_unsuccessful_payment = matching_pg_payment.message
Expand All @@ -404,7 +405,7 @@ def update_payment(
_payment.delivered_quantity_usd = get_quantity_in_usd(
amount=Decimal(delivered_quantity),
currency=_payment_plan.currency,
exchange_rate=Decimal(exchange_rate),
exchange_rate=Decimal(_exchange_rate),
currency_exchange_date=_payment_plan.currency_exchange_date,
)
except (ValueError, TypeError):
Expand All @@ -427,9 +428,11 @@ def update_payment(
if not payment_plan.is_reconciled:
if payment_plan.splits.exists():
for split in payment_plan.splits.filter(sent_to_payment_gateway=True):
pg_payment_records = self.api.get_records_for_payment_instruction(split.id)
for payment in split.payments.filter(status=Payment.STATUS_PENDING).order_by("unicef_id"):
update_payment(payment, pg_payment_records, split, payment_plan, exchange_rate)
pending_payments = split.payments.filter(status=Payment.STATUS_PENDING).order_by("unicef_id")
if pending_payments.exists():
pg_payment_records = self.api.get_records_for_payment_instruction(split.id)
for payment in pending_payments:
update_payment(payment, pg_payment_records, split, payment_plan, exchange_rate)
else:
for delivery_mechanism in payment_plan.delivery_mechanisms.filter(
financial_service_provider__communication_channel=FinancialServiceProvider.COMMUNICATION_CHANNEL_API,
Expand All @@ -440,6 +443,9 @@ def update_payment(
financial_service_provider=delivery_mechanism.financial_service_provider,
status=Payment.STATUS_PENDING,
).order_by("unicef_id")
pg_payment_records = self.api.get_records_for_payment_instruction(delivery_mechanism.id)
for payment in pending_payments:
update_payment(payment, pg_payment_records, delivery_mechanism, payment_plan, exchange_rate)
if pending_payments.exists():
pg_payment_records = self.api.get_records_for_payment_instruction(delivery_mechanism.id)
for payment in pending_payments:
update_payment(
payment, pg_payment_records, delivery_mechanism, payment_plan, exchange_rate
)
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
'deliveredQuantityUsd': 100.0,
'entitlementQuantity': 100.0,
'entitlementQuantityUsd': 200.0,
'fspAuthCode': '',
'parent': {
'unicefId': 'PP-02'
},
Expand Down Expand Up @@ -216,6 +217,7 @@
'deliveredQuantityUsd': 100.0,
'entitlementQuantity': 100.0,
'entitlementQuantityUsd': 200.0,
'fspAuthCode': '123',
'parent': {
'unicefId': 'PP-01'
},
Expand All @@ -235,6 +237,7 @@
'deliveredQuantityUsd': 100.0,
'entitlementQuantity': 100.0,
'entitlementQuantityUsd': 200.0,
'fspAuthCode': '',
'parent': {
'unicefId': 'PP-01'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class TestPaymentPlanQueries(APITestCase):
paymentPlanEndDate
}
unicefId
fspAuthCode
}
}
totalCount
Expand Down Expand Up @@ -232,7 +233,7 @@ def setUpTestData(cls) -> None:
hoh2 = IndividualFactory(household=None)
hh1 = HouseholdFactory(head_of_household=hoh1)
hh2 = HouseholdFactory(head_of_household=hoh2)
PaymentFactory(
cls.p1 = PaymentFactory(
parent=cls.pp,
conflicted=False,
household=hh1,
Expand All @@ -242,8 +243,9 @@ def setUpTestData(cls) -> None:
delivered_quantity=50.00,
delivered_quantity_usd=100.00,
currency="PLN",
fsp_auth_code="123",
)
p2 = PaymentFactory(
cls.p2 = PaymentFactory(
parent=cls.pp,
conflicted=True,
household=hh2,
Expand All @@ -253,6 +255,7 @@ def setUpTestData(cls) -> None:
delivered_quantity=50.00,
delivered_quantity_usd=100.00,
currency="PLN",
fsp_auth_code=None,
)

# create hard conflicted payment
Expand All @@ -270,14 +273,15 @@ def setUpTestData(cls) -> None:

PaymentFactory(
parent=cls.pp_conflicted,
household=p2.household,
household=cls.p2.household,
conflicted=False,
entitlement_quantity=100.00,
entitlement_quantity_usd=200.00,
delivered_quantity=50.00,
delivered_quantity_usd=100.00,
financial_service_provider=FinancialServiceProviderFactory(name="xxx"),
currency="PLN",
fsp_auth_code="789",
)
PaymentFactory(
parent=cls.pp_conflicted,
Expand All @@ -288,6 +292,7 @@ def setUpTestData(cls) -> None:
delivered_quantity_usd=00.00,
financial_service_provider=FinancialServiceProviderFactory(name="yyy"),
currency="PLN",
fsp_auth_code="987",
)

IndividualFactory(household=hh1, sex="FEMALE", birth_date=datetime.now().date() - relativedelta(years=5))
Expand Down Expand Up @@ -326,6 +331,12 @@ def test_fetch_all_payment_plans(self) -> None:

@freeze_time("2020-10-10")
def test_fetch_all_payments_for_open_payment_plan(self) -> None:
from hct_mis_api.apps.account.models import UserRole

role = UserRole.objects.get(user=self.user).role
role.permissions.append("PM_VIEW_FSP_AUTH_CODE")
role.save()

self.snapshot_graphql_request(
request_string=self.ALL_PAYMENTS_QUERY,
context={"user": self.user},
Expand Down
Loading

0 comments on commit 72c6ffe

Please sign in to comment.