Skip to content

Commit

Permalink
Add API for latest application's applicant
Browse files Browse the repository at this point in the history
In order to make accesible the latest applicant's address information
added the API endpoint.
Save applicantion's address to application instead of using the profile
address, this change was needed when the user would delete
his address from his profile making it unaccesible from
the application.
  • Loading branch information
rbreve committed Oct 28, 2024
1 parent edcbe17 commit e778790
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 51 deletions.
6 changes: 5 additions & 1 deletion application_form/api/sales/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,21 @@ class ProjectUUIDSerializer(serializers.Serializer):


class SalesApplicantSerializer(ApplicantSerializerBase):
class Meta(ApplicantSerializerBase.Meta):
fields = ApplicantSerializerBase.Meta.fields + ["date_of_birth", "ssn_suffix"]

pass


class SalesApplicationSerializer(ApplicationSerializerBase):
profile = serializers.PrimaryKeyRelatedField(
queryset=Profile.objects.all(), write_only=True
)
applicant = SalesApplicantSerializer(write_only=True)
additional_applicant = SalesApplicantSerializer(write_only=True, allow_null=True)

class Meta(ApplicationSerializerBase.Meta):
fields = ApplicationSerializerBase.Meta.fields + ("profile",)
fields = ApplicationSerializerBase.Meta.fields + ("profile", "applicant")

def _get_senders_name_from_applicants_data(self, validated_data):
additional_applicant = validated_data.get("additional_applicant")
Expand Down
39 changes: 23 additions & 16 deletions application_form/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from enumfields.drf import EnumField, EnumSupportSerializerMixin
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, IntegerField, UUIDField
from rest_framework.fields import IntegerField, UUIDField

from apartment_application_service.settings import (
METADATA_HANDLER_INFORMATION,
Expand Down Expand Up @@ -36,8 +36,6 @@


class ApplicantSerializerBase(serializers.ModelSerializer):
date_of_birth = serializers.DateField(write_only=True)

class Meta:
model = Applicant
fields = [
Expand All @@ -49,13 +47,16 @@ class Meta:
"city",
"postal_code",
"age",
"date_of_birth",
"ssn_suffix",
]
extra_kwargs = {"age": {"read_only": True}}


class ApplicantSerializer(ApplicantSerializerBase):
date_of_birth = serializers.DateField(write_only=True)

class Meta(ApplicantSerializerBase.Meta):
fields = ApplicantSerializerBase.Meta.fields + ["date_of_birth", "ssn_suffix"]

def validate(self, attrs):
super().validate(attrs)
date_of_birth = attrs.get("date_of_birth")
Expand All @@ -81,15 +82,13 @@ class ApplicationSerializerBase(serializers.ModelSerializer):
application_uuid = UUIDField(source="external_uuid")
application_type = EnumField(ApplicationType, source="type", write_only=True)
project_id = UUIDField(write_only=True)
ssn_suffix = CharField(write_only=True, min_length=5, max_length=5)
apartments = ApplicationApartmentSerializer(write_only=True, many=True)

class Meta:
model = Application
fields = (
"application_uuid",
"application_type",
"ssn_suffix",
"has_children",
"additional_applicant",
"right_of_residence",
Expand Down Expand Up @@ -125,9 +124,16 @@ def prepare_metadata(self, validated_data):


class ApplicationSerializer(ApplicationSerializerBase):
applicant = ApplicantSerializer(write_only=True)
additional_applicant = ApplicantSerializer(write_only=True, allow_null=True)
project_id = UUIDField(write_only=True)

class Meta(ApplicationSerializerBase.Meta):
fields = ApplicationSerializerBase.Meta.fields + (
"applicant",
"additional_applicant",
)

def _get_senders_name_from_applicants_data(self, validated_data):
additional_applicant = validated_data.get("additional_applicant")
sender_names = validated_data["profile"].full_name
Expand Down Expand Up @@ -169,17 +175,18 @@ def validate_ssn_suffix(self, value):
def validate(self, attrs):
project_uuid = attrs["project_id"]
applicants = []
profile = self.context["request"].user.profile
applicants.append((profile.date_of_birth, attrs["ssn_suffix"]))
is_additional_applicant = (
"additional_applicant" in attrs
and attrs["additional_applicant"] is not None
)
if is_additional_applicant:
applicant_data = attrs.get("applicant")
if applicant_data:
applicants.append(
(applicant_data["date_of_birth"], applicant_data["ssn_suffix"])
)

additional_applicant = attrs.get("additional_applicant")
if additional_applicant:
applicants.append(
(
attrs["additional_applicant"]["date_of_birth"],
attrs["additional_applicant"]["ssn_suffix"],
additional_applicant["date_of_birth"],
additional_applicant["ssn_suffix"],
)
)
validator = ProjectApplicantValidator()
Expand Down
27 changes: 25 additions & 2 deletions application_form/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from rest_framework_simplejwt.authentication import JWTAuthentication

from apartment.elastic.queries import get_apartment_uuids
from application_form.api.serializers import ApartmentReservationSerializer
from application_form.api.serializers import (
ApartmentReservationSerializer,
ApplicationSerializer,
ApplicantSerializerBase as ApplicantSerializer,
)
from application_form.api.serializers import ApplicationSerializer
from application_form.models import ApartmentReservation, Application
from audit_log.viewsets import AuditLoggingModelViewSet

Expand Down Expand Up @@ -40,3 +41,25 @@ def get(self, request, project_uuid):
)
serializer = self.get_serializer(reservations, many=True)
return Response(serializer.data)


class LatestApplicantInfo(GenericAPIView):
"""
Returns the primary applicant from the latest application.
"""

serializer_class = ApplicantSerializer
http_method_names = ["get"]

def get(self, request, customer_id):
application = Application.objects.filter(customer__id=customer_id).latest(
"created_at"
)
applicant = application.applicants.filter(is_primary_applicant=True).first()
if application:
applicant = application.applicants.filter(is_primary_applicant=True).first()
if applicant:
serializer = self.get_serializer(applicant)
return Response(serializer.data)

return Response({})
29 changes: 16 additions & 13 deletions application_form/services/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def create_application(
)
data = application_data.copy()
profile = data.pop("profile")
additional_applicant_data = data.pop("additional_applicant")
applicant_data = data.pop("applicant")
additional_applicant_data = data.pop("additional_applicant", None)
customer = get_or_create_customer_from_profiles(
profile, additional_applicant_data, data.get("has_children")
)
Expand All @@ -116,17 +117,19 @@ def create_application(
sender_names=data.pop("sender_names"),
)
Applicant.objects.create(
first_name=profile.first_name,
last_name=profile.last_name,
email=profile.email,
phone_number=profile.phone_number,
street_address=profile.street_address,
city=profile.city,
postal_code=profile.postal_code,
age=_calculate_age(profile.date_of_birth),
date_of_birth=profile.date_of_birth,
ssn_suffix=application_data["ssn_suffix"],
contact_language=profile.contact_language,
first_name=applicant_data["first_name"],
last_name=applicant_data["last_name"],
email=applicant_data["email"],
phone_number=applicant_data["phone_number"],
street_address=applicant_data["street_address"],
city=applicant_data["city"],
postal_code=applicant_data["postal_code"],
age=_calculate_age(applicant_data["date_of_birth"]),
date_of_birth=applicant_data["date_of_birth"],
ssn_suffix=applicant_data["ssn_suffix"],
contact_language=applicant_data.get(
"contact_language", profile.contact_language
),
is_primary_applicant=True,
application=application,
)
Expand Down Expand Up @@ -166,7 +169,7 @@ def update_profile_from_application_data(profile: Profile, data: dict) -> None:
Currently only fills the ssn_suffix field if it's empty in the
profile.
"""
ssn_suffix = data.get("ssn_suffix")
ssn_suffix = data.get("applicant", {}).get("ssn_suffix")
if not profile.ssn_suffix and ssn_suffix:
profile.ssn_suffix = ssn_suffix
profile.save(update_fields=["ssn_suffix"])
Expand Down
29 changes: 19 additions & 10 deletions application_form/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,24 @@ def create_application_data(
for index, apartment_uuid in enumerate(apartment_uuids[0:5])
]
right_of_residence = 123456 if application_type == ApplicationType.HASO else None

# Build application request data
application_data = {
"application_uuid": str(uuid.uuid4()),
"application_type": application_type.value,
"ssn_suffix": profile.ssn_suffix,
"has_children": True,
"right_of_residence": right_of_residence,
"additional_applicant": None,
"applicant": {
"first_name": profile.first_name,
"last_name": profile.last_name,
"email": profile.email,
"street_address": profile.street_address,
"postal_code": profile.postal_code,
"city": profile.city,
"phone_number": profile.phone_number,
"date_of_birth": profile.date_of_birth,
"ssn_suffix": profile.ssn_suffix,
},
"project_id": str(project_uuid),
"apartments": apartments_data,
"has_hitas_ownership": True,
Expand All @@ -260,15 +269,15 @@ def create_application_data(
# Add a second applicant if needed
if num_applicants == 2:
date_of_birth = faker.Faker().date_of_birth(minimum_age=18)
applicant = ApplicantFactory.build()
additional_applicant = ApplicantFactory.build()
application_data["additional_applicant"] = {
"first_name": applicant.first_name,
"last_name": applicant.last_name,
"email": applicant.email,
"street_address": applicant.street_address,
"postal_code": applicant.postal_code,
"city": applicant.city,
"phone_number": applicant.phone_number,
"first_name": additional_applicant.first_name,
"last_name": additional_applicant.last_name,
"email": additional_applicant.email,
"street_address": additional_applicant.street_address,
"postal_code": additional_applicant.postal_code,
"city": additional_applicant.city,
"phone_number": additional_applicant.phone_number,
"date_of_birth": f"{date_of_birth:%Y-%m-%d}",
"ssn_suffix": calculate_ssn_suffix(date_of_birth),
}
Expand Down
14 changes: 7 additions & 7 deletions application_form/tests/test_application_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def test_application_post_sets_nin(api_client, elastic_single_project_with_apart
assert response.status_code == 201
assert response.data == {"application_uuid": data["application_uuid"]}
profile.refresh_from_db()
assert profile.ssn_suffix == data["ssn_suffix"]
assert profile.ssn_suffix == data["applicant"]["ssn_suffix"]
assert len(profile.national_identification_number) == 11


Expand Down Expand Up @@ -212,14 +212,14 @@ def test_application_post_fails_if_incorrect_ssn_suffix(
profile = ProfileFactory()
api_client.credentials(HTTP_AUTHORIZATION=f"Bearer {_create_token(profile)}")
data = create_application_data(profile)
data["ssn_suffix"] = "-000$"
data["applicant"]["ssn_suffix"] = "-000$"
response = api_client.post(
reverse("application_form:application-list"), data, format="json"
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert len(response.data["ssn_suffix"][0]["message"]) > 0
assert len(response.data["applicant"]["ssn_suffix"][0]["message"]) > 0
assert (
response.data["ssn_suffix"][0]["code"]
response.data["applicant"]["ssn_suffix"][0]["code"]
== error_codes.E1000_SSN_SUFFIX_IS_NOT_VALID
)

Expand Down Expand Up @@ -350,9 +350,9 @@ def test_application_post_fails_if_partner_profile_have_already_applied_to_proje
).date(),
)
partner_application_data = create_application_data(partner_profile)
partner_application_data["ssn_suffix"] = application_data["additional_applicant"][
"ssn_suffix"
]
partner_application_data["applicant"]["ssn_suffix"] = application_data[
"additional_applicant"
]["ssn_suffix"]
api_client.credentials(
HTTP_AUTHORIZATION=f"Bearer {_create_token(partner_profile)}"
)
Expand Down
2 changes: 1 addition & 1 deletion application_form/tests/test_sales_application_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_sales_application_post(
)
data = create_application_data(customer_profile)
data["profile"] = customer_profile.id
data["ssn_suffix"] = "XXXXX" # ssn suffix should not be validated
data["applicant"]["ssn_suffix"] = "XXXXX" # ssn suffix should not be validated
data["additional_applicant"]["ssn_suffix"] = "XXXXX"
response = drupal_salesperson_api_client.post(
reverse("application_form:sales-application-list"), data, format="json"
Expand Down
11 changes: 10 additions & 1 deletion application_form/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
OfferViewSet,
SalesApplicationViewSet,
)
from application_form.api.views import ApplicationViewSet, ListProjectReservations
from application_form.api.views import (
ApplicationViewSet,
LatestApplicantInfo,
ListProjectReservations,
)
from invoicing.api.views import (
ApartmentInstallmentAddToSapAPIView,
ApartmentInstallmentAPIView,
Expand Down Expand Up @@ -70,6 +74,11 @@
apartment_states,
name="apartment_states",
),
path(
r"sales/applicant/latest/<int:customer_id>/",
LatestApplicantInfo.as_view(),
name="applicant-by-customer",
),
path("", include(router.urls)),
]
urlpatterns += public_urlpatterns
7 changes: 7 additions & 0 deletions asko_import/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ class Meta:
"street_address",
)

def __init__(self, *args, **kwargs):
exclude_fields = kwargs.pop("exclude_fields", None)
super().__init__(*args, **kwargs)
if exclude_fields:
for field_name in exclude_fields:
self.fields.pop(field_name, None)


class ApplicationSerializer(EnumSupportSerializerMixin, serializers.ModelSerializer):
customer = CustomPrimaryKeyRelatedField(queryset=Customer.objects.all())
Expand Down

0 comments on commit e778790

Please sign in to comment.