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

hotfix_add_financial_payment_provider_inline_validation #4193

Merged
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
39 changes: 39 additions & 0 deletions src/hct_mis_api/apps/payment/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,50 @@ def has_add_permission(self, request: HttpRequest) -> bool:
return request.user.can_change_fsp()


class FspXlsxTemplatePerDeliveryMechanismForm(forms.ModelForm):
class Meta:
model = FspXlsxTemplatePerDeliveryMechanism
fields = ("financial_service_provider", "delivery_mechanism", "xlsx_template")

def clean(self) -> Optional[Dict[str, Any]]:
cleaned_data = super().clean()
delivery_mechanism = cleaned_data.get("delivery_mechanism")
financial_service_provider = cleaned_data.get("financial_service_provider")
xlsx_template = cleaned_data.get("xlsx_template")

if not delivery_mechanism or not financial_service_provider:
return cleaned_data

missing_required_core_fields = [
required_field
for required_field in delivery_mechanism.required_fields
if required_field not in xlsx_template.core_fields
]
if missing_required_core_fields:
raise ValidationError(
f"{missing_required_core_fields} fields are required by delivery mechanism "
f"{delivery_mechanism} and must be present in the template core fields"
)

error_message = f"Delivery Mechanism {delivery_mechanism} is not supported by Financial Service Provider {financial_service_provider}"
# to work both in inline and standalone
if delivery_mechanisms := self.data.get("delivery_mechanisms"):
if delivery_mechanism and str(delivery_mechanism.id) not in delivery_mechanisms:
raise ValidationError(error_message)
else:
if delivery_mechanism and delivery_mechanism not in financial_service_provider.delivery_mechanisms.all():
raise ValidationError(error_message)

return cleaned_data


@admin.register(FspXlsxTemplatePerDeliveryMechanism)
class FspXlsxTemplatePerDeliveryMechanismAdmin(HOPEModelAdminBase):
list_display = ("financial_service_provider", "delivery_mechanism", "xlsx_template", "created_by")
fields = ("financial_service_provider", "delivery_mechanism", "xlsx_template")
autocomplete_fields = ("financial_service_provider", "xlsx_template")
exclude = ("delivery_mechanism_choice",)
form = FspXlsxTemplatePerDeliveryMechanismForm

def save_model(
self, request: HttpRequest, obj: FspXlsxTemplatePerDeliveryMechanism, form: "Form", change: bool
Expand Down Expand Up @@ -405,6 +443,7 @@ def clean(self) -> Optional[Dict[str, Any]]:


class FspXlsxTemplatePerDeliveryMechanismAdminInline(admin.TabularInline):
form = FspXlsxTemplatePerDeliveryMechanismForm
model = FspXlsxTemplatePerDeliveryMechanism
extra = 0
readonly_fields = ("created_by",)
Expand Down
17 changes: 0 additions & 17 deletions src/hct_mis_api/apps/payment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1354,23 +1354,6 @@ class Meta:
def __str__(self) -> str:
return f"{self.financial_service_provider.name} - {self.xlsx_template} - {self.delivery_mechanism}" # pragma: no cover

def clean(self) -> None:
missing_required_core_fields = [
required_field
for required_field in self.delivery_mechanism.required_fields
if required_field not in self.xlsx_template.core_fields
]
if missing_required_core_fields:
raise ValidationError(
f"{missing_required_core_fields} fields are required by delivery mechanism "
f"{self.delivery_mechanism} and must be present in the template core fields"
)

if self.delivery_mechanism not in self.financial_service_provider.delivery_mechanisms.all():
raise ValidationError(
f"Delivery Mechanism {self.delivery_mechanism} is not supported by Financial Service Provider {self.financial_service_provider}"
)


class FinancialServiceProvider(LimitBusinessAreaModelMixin, TimeStampedUUIDModel):
COMMUNICATION_CHANNEL_API = "API"
Expand Down
91 changes: 65 additions & 26 deletions tests/unit/apps/grievance/test_model_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from hct_mis_api.apps.core.fixtures import create_afghanistan
from hct_mis_api.apps.core.models import BusinessArea
from hct_mis_api.apps.grievance.models import GrievanceTicket
from hct_mis_api.apps.payment.admin import FspXlsxTemplatePerDeliveryMechanismForm
from hct_mis_api.apps.payment.fixtures import (
FinancialServiceProviderFactory,
FinancialServiceProviderXlsxTemplateFactory,
FspXlsxTemplatePerDeliveryMechanismFactory,
generate_delivery_mechanisms,
)
from hct_mis_api.apps.payment.models import DeliveryMechanism
from hct_mis_api.apps.payment.models import DeliveryMechanism, FinancialServiceProvider


class TestGrievanceModelValidation(TestCase):
Expand Down Expand Up @@ -84,38 +84,77 @@ def setUpTestData(cls) -> None:
create_afghanistan()
cls.user = UserFactory.create()
generate_delivery_mechanisms()
cls.dm_transfer_to_account = DeliveryMechanism.objects.get(code="transfer_to_account")

def test_clean(self) -> None:
dm_cash = DeliveryMechanism.objects.get(code="cash")
dm_atm_card = DeliveryMechanism.objects.get(code="atm_card")
fsp = FinancialServiceProviderFactory()
fsp.delivery_mechanisms.set([dm_atm_card])
template = FinancialServiceProviderXlsxTemplateFactory()
template_per_dm_cash = FspXlsxTemplatePerDeliveryMechanismFactory(
financial_service_provider=fsp,
delivery_mechanism=dm_cash,
xlsx_template=template,
def test_admin_form_clean(self) -> None:
fsp_xls_template = FinancialServiceProviderXlsxTemplateFactory(
core_fields=["bank_name__transfer_to_account", "bank_account_number__transfer_to_account"]
)

fsp = FinancialServiceProviderFactory(
name="Test FSP",
vision_vendor_number="123",
communication_channel=FinancialServiceProvider.COMMUNICATION_CHANNEL_API,
)
fsp.delivery_mechanisms.add(self.dm_transfer_to_account)

# test valid form
form_data_standalone = {
"financial_service_provider": fsp.id,
"delivery_mechanism": self.dm_transfer_to_account.id,
"xlsx_template": fsp_xls_template.id,
}
form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone)
self.assertTrue(form.is_valid())
form.clean()

# test inline form data valid
form_data_inline = {
"financial_service_provider": fsp.id,
"delivery_mechanism": self.dm_transfer_to_account.id,
"xlsx_template": fsp_xls_template.id,
"delivery_mechanisms": [str(self.dm_transfer_to_account.id)],
}
form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_inline)
self.assertTrue(form.is_valid())
form.clean()

# test missing required core fields
fsp_xls_template.core_fields = []
fsp_xls_template.save()

form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone)
self.assertFalse(form.is_valid())
with self.assertRaisesMessage(
ValidationError,
f"Delivery Mechanism {template_per_dm_cash.delivery_mechanism} is not supported by Financial Service Provider {fsp}",
"[\"['bank_name__transfer_to_account', 'bank_account_number__transfer_to_account'] fields are required by delivery mechanism Transfer to Account and must be present in the template core fields\"]",
):
template_per_dm_cash.clean()
form.clean()

template_per_dm_atm_card = FspXlsxTemplatePerDeliveryMechanismFactory(
financial_service_provider=fsp,
delivery_mechanism=dm_atm_card,
xlsx_template=template,
)
fsp_xls_template.core_fields = ["bank_name__transfer_to_account", "bank_account_number__transfer_to_account"]
fsp_xls_template.save()

# test delivery mechanism not supported
fsp.delivery_mechanisms.remove(self.dm_transfer_to_account)
form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone)
self.assertFalse(form.is_valid())
with self.assertRaisesMessage(
ValidationError,
f"['card_number__atm_card', 'card_expiry_date__atm_card', 'name_of_cardholder__atm_card'] fields are required by delivery mechanism "
f"{template_per_dm_atm_card.delivery_mechanism} and must be present in the template core fields",
"['Delivery Mechanism Transfer to Account is not supported by Financial Service Provider Test FSP (123): API']",
):
template_per_dm_atm_card.clean()

template.core_fields = ["card_number__atm_card", "card_expiry_date__atm_card", "name_of_cardholder__atm_card"]
template.save()
template_per_dm_atm_card.clean()
form.clean()

# test inline form data invalid
form_data_inline = {
"financial_service_provider": fsp.id,
"delivery_mechanism": self.dm_transfer_to_account.id,
"xlsx_template": fsp_xls_template.id,
"delivery_mechanisms": ["12313213123"],
}
form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_inline)
self.assertFalse(form.is_valid())
with self.assertRaisesMessage(
ValidationError,
"['Delivery Mechanism Transfer to Account is not supported by Financial Service Provider Test FSP (123): API']",
):
form.clean()
Loading
Loading