Skip to content

Commit

Permalink
Merge branch 'develop' into ephemeral-1
Browse files Browse the repository at this point in the history
  • Loading branch information
pavlo-mk committed Oct 30, 2024
2 parents e98b939 + a08563b commit 84a1d3d
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 52 deletions.
16 changes: 15 additions & 1 deletion src/frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const api = {
return params.toString();
},

async get(url: string, params: Record<string, any> = {}) {
async get(url: string, params: Record<string, any> = {}, filename?: string) {
const query = this.buildParams(params);
const cacheKey = url + (query ? `?${query}` : '');

Expand All @@ -42,6 +42,20 @@ export const api = {
throw new Error(`Error fetching data from ${url}`);
}

// Handle download if URL includes "download"
if (url.includes('download')) {
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = filename || url.split('/').pop() || 'download';
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(downloadUrl);
return;
}

const etag = response.headers.get('ETag');
const data = await response.json();

Expand Down
16 changes: 12 additions & 4 deletions src/frontend/src/api/paymentModuleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,17 @@ export const fetchSupportingDocument = async (
programId: string,
paymentPlanId: string,
fileId: string,
fileName: string,
): Promise<any> => {
const response = await api.get(
`${businessAreaSlug}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/download/`,
);
return response;
try {
const response = await api.get(
`${businessAreaSlug}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/download/`,
{},
fileName,
);
return response;
} catch (error: any) {
const errorMessage = error?.message || 'An unknown error occurred';
throw new Error(`Failed to fetch supporting document: ${errorMessage}`);
}
};
2 changes: 2 additions & 0 deletions src/frontend/src/api/periodicDataUpdateApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const fetchPeriodicDataUpdateTemplate = async (
): Promise<any> => {
const response = await api.get(
`${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-data-update-templates/${templateId}/download/`,
{},
`Periodic_Data_Update_Template_${templateId}`,
);
return response;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,16 @@ export const SupportingDocumentsSection = ({
'Are you sure you want to delete this file? This action cannot be reversed.',
);

const handleSupportingDocumentDownloadClick = (fileId: string) => {
const handleSupportingDocumentDownloadClick = (
fileId: string,
fileName: string,
) => {
downloadSupportingDocument({
businessAreaSlug: businessArea,
programId,
paymentPlanId: paymentPlan.id,
fileId: fileId.toString(),
fileName: fileName,
});
};

Expand Down Expand Up @@ -273,7 +277,10 @@ export const SupportingDocumentsSection = ({
{canDownloadFile && (
<IconButton
onClick={() =>
handleSupportingDocumentDownloadClick(doc.id)
handleSupportingDocumentDownloadClick(
doc.id,
doc.file,
)
}
data-cy="download-button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,21 @@ export const useDownloadSupportingDocument = () => {
programId: mutationProgramId,
paymentPlanId: mutationPaymentPlanId,
fileId: mutationFileId,
fileName: mutationFileName,
}: {
businessAreaSlug: string;
programId: string;
paymentPlanId: string;
fileId: string;
fileName: string;
}) => {
return fetchSupportingDocument(
mutationBusinessAreaSlug,
mutationProgramId,
mutationPaymentPlanId,
mutationFileId,
mutationFileName,
);
},
onSuccess: (data) => {
if (data.url) {
window.open(data.url);
}
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '@mui/material';
import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList';
import { useQuery } from '@tanstack/react-query';
import React, { FC } from 'react';
import { FC } from 'react';
import { useTranslation } from 'react-i18next';

interface PeriodicDataUpdatesTemplateDetailsDialogProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
import { useMutation, useQueryClient } from '@tanstack/react-query';

export const useDownloadPeriodicDataUpdateTemplate = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
businessAreaSlug: mutationBusinessAreaSlug,
Expand All @@ -23,14 +22,6 @@ export const useDownloadPeriodicDataUpdateTemplate = () => {
mutationTemplateId,
);
},
onSuccess: (data) => {
if (data.url) {
window.open(data.url);
}
queryClient.invalidateQueries({
queryKey: ['periodicDataUpdateTemplates'],
});
},
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def mark_as_distinct_individual(
individual_to_distinct,
)
individual_marked_as_distinct.send(sender=Individual, instance=individual_to_distinct)
household = individual_to_distinct.household
household.refresh_from_db()
if household.active_individuals.count() > 0:
household.unwithdraw()
if household := individual_to_distinct.household:
household.refresh_from_db()
if household.active_individuals.count() > 0:
household.unwithdraw()
12 changes: 11 additions & 1 deletion src/hct_mis_api/apps/payment/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import base64
from typing import Any, Dict, Optional

from django.shortcuts import get_object_or_404

from rest_framework import serializers

from hct_mis_api.apps.account.api.fields import Base64ModelField
from hct_mis_api.apps.core.utils import decode_id_string
from hct_mis_api.apps.payment.models import PaymentPlan, PaymentPlanSupportingDocument


Expand Down Expand Up @@ -61,6 +64,7 @@ class PaymentPlanBulkActionSerializer(serializers.Serializer):

class PaymentPlanSupportingDocumentSerializer(serializers.ModelSerializer):
id = serializers.SerializerMethodField()
file = serializers.FileField(use_url=False)

class Meta:
model = PaymentPlanSupportingDocument
Expand All @@ -81,7 +85,10 @@ def validate_file(self, file: Any) -> Any:
return file

def validate(self, data: Dict) -> Dict:
payment_plan = self.context["payment_plan"]
payment_plan_id = self.context["request"].parser_context["kwargs"]["payment_plan_id"]
payment_plan = get_object_or_404(PaymentPlan, id=decode_id_string(payment_plan_id))
data["payment_plan"] = payment_plan
data["created_by"] = self.context["request"].user
if payment_plan.status not in [PaymentPlan.Status.OPEN, PaymentPlan.Status.LOCKED]:
raise serializers.ValidationError("Payment plan must be within status OPEN or LOCKED.")

Expand All @@ -90,3 +97,6 @@ def validate(self, data: Dict) -> Dict:
f"Payment plan already has the maximum of {PaymentPlanSupportingDocument.FILE_LIMIT} supporting documents."
)
return data

def create(self, validated_data: Dict[str, Any]) -> PaymentPlanSupportingDocument:
return super().create(validated_data)
18 changes: 7 additions & 11 deletions src/hct_mis_api/apps/payment/api/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import logging
import mimetypes
from typing import Any, Optional

from django.db import transaction
from django.db.models import QuerySet
from django.http import FileResponse

from constance import config
from django_filters import rest_framework as filters
Expand Down Expand Up @@ -199,22 +201,16 @@ def get_object(self) -> PaymentPlanSupportingDocument:
PaymentPlanSupportingDocument, id=decode_id_string(self.kwargs.get("file_id")), payment_plan=payment_plan
)

def create(self, request: Request, *args: Any, **kwargs: Any) -> Response:
payment_plan = get_object_or_404(PaymentPlan, id=decode_id_string(kwargs.get("payment_plan_id")))
request.data["created_by"] = request.user.pk
serializer = self.get_serializer(data=request.data, context={"payment_plan": payment_plan})
if serializer.is_valid():
serializer.save(payment_plan=payment_plan)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response:
self.permission_classes = [PaymentPlanSupportingDocumentDeletePermission]
document = self.get_object()
document.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

@action(detail=True, methods=["get"])
def download(self, request: Request, *args: Any, **kwargs: Any) -> Response:
def download(self, request: Request, *args: Any, **kwargs: Any) -> FileResponse:
document = self.get_object()
return Response({"url": document.file.url}, status=status.HTTP_200_OK)
file_mimetype, _ = mimetypes.guess_type(document.file.path)
response = FileResponse(document.file.open(), content_type=file_mimetype or "application/octet-stream")
response["Content-Disposition"] = f"attachment; filename={document.file.name.split('/')[-1]}"
return response
10 changes: 8 additions & 2 deletions src/hct_mis_api/apps/periodic_data_update/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.core.exceptions import ValidationError as DjangoValidationError
from django.db.models import QuerySet
from django.http import FileResponse

from constance import config
from django_filters.rest_framework import DjangoFilterBackend
Expand Down Expand Up @@ -104,7 +105,7 @@ def export(self, request: Request, *args: Any, **kwargs: Any) -> Response:
return Response(status=status.HTTP_200_OK, data={"message": "Exporting template"})

@action(detail=True, methods=["get"])
def download(self, request: Request, *args: Any, **kwargs: Any) -> Response:
def download(self, request: Request, *args: Any, **kwargs: Any) -> FileResponse:
pdu_template = self.get_object()

if pdu_template.status != PeriodicDataUpdateTemplate.Status.EXPORTED:
Expand All @@ -115,7 +116,12 @@ def download(self, request: Request, *args: Any, **kwargs: Any) -> Response:
logger.error(f"XLSX File not found. PeriodicDataUpdateTemplate ID: {pdu_template.id}")
raise ValidationError("Template file is missing")

return Response({"url": pdu_template.file.file.url}, status=status.HTTP_200_OK)
return FileResponse(
pdu_template.file.file.open(),
as_attachment=True,
filename=pdu_template.file.file.name,
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)


class PeriodicDataUpdateUploadViewSet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,15 +762,12 @@ def test_grievance_tickets_create_new_ticket_Data_Change_Add_Individual_Mandator
assert "-" in pageGrievanceDetailsPage.getTicketAssigment().text
assert "Data Change" in pageGrievanceDetailsPage.getTicketCategory().text
assert "Add Individual" in pageGrievanceDetailsPage.getLabelIssueType().text
assert household_without_disabilities.unicef_id in pageGrievanceDetailsPage.getTicketHouseholdID().text
assert household_without_disabilities.unicef_id in pageGrievanceDetailsPage.getTicketTargetID().text
assert "Test Program" in pageGrievanceDetailsPage.getLabelProgramme().text
assert datetime.now().strftime("%-d %b %Y") in pageGrievanceDetailsPage.getLabelDateCreation().text
assert datetime.now().strftime("%-d %b %Y") in pageGrievanceDetailsPage.getLabelLastModifiedDate().text
assert "-" in pageGrievanceDetailsPage.getLabelAdministrativeLevel2().text
assert "-" in pageGrievanceDetailsPage.getLabelLanguagesSpoken().text
assert "-" in pageGrievanceDetailsPage.getLabelDocumentation().text
assert "Add Individual - TEST" in pageGrievanceDetailsPage.getLabelDescription().text
assert "-" in pageGrievanceDetailsPage.getLabelComments().text
assert "Male" in pageGrievanceDetailsPage.getLabelGender().text
assert "Alternate collector" in pageGrievanceDetailsPage.getLabelRole().text
assert "Krido" in pageGrievanceDetailsPage.getLabelFullName().text
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/apps/grievance/test_services_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,53 @@ def test_close_needs_adjudication_ticket_service(self) -> None:
assert ind_1.duplicate is False
assert ind_2.duplicate is True

def test_close_needs_adjudication_ticket_service_individual_without_household(self) -> None:
user = UserFactory()
ba = BusinessAreaFactory(slug="afghanistan")
program = ProgramFactory(business_area=ba)

grievance = GrievanceTicketFactory(
category=GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION,
business_area=ba,
status=GrievanceTicket.STATUS_FOR_APPROVAL,
description="GrievanceTicket",
issue_type=GrievanceTicket.ISSUE_TYPE_UNIQUE_IDENTIFIERS_SIMILARITY,
)
grievance.programs.add(program)
ind_data = {
"given_name": "John",
"family_name": "Doe",
"middle_name": "",
"full_name": "John Doe",
}
ind_1 = IndividualFactory(household=None, program=program, **ind_data)
document = DocumentFactory(individual=ind_1, status=Document.STATUS_INVALID)
_, individuals_2 = create_household(
{"size": 1, "business_area": ba, "program": program},
ind_data,
)
ind_2 = individuals_2[0]

ticket_details = TicketNeedsAdjudicationDetailsFactory(
ticket=grievance,
golden_records_individual=ind_1,
is_multiple_duplicates_version=True,
selected_individual=None,
)
ticket_details.selected_distinct.set([ind_1, ind_2])
ticket_details.ticket = grievance
ticket_details.save()

close_needs_adjudication_ticket_service(grievance, user)

ind_1.refresh_from_db()
ind_2.refresh_from_db()
document.refresh_from_db()

self.assertEqual(ind_1.duplicate, False)
self.assertEqual(ind_2.duplicate, False)
self.assertEqual(document.status, Document.STATUS_VALID)

def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> None:
user = UserFactory()
ba = BusinessAreaFactory(slug="afghanistan")
Expand Down
Loading

0 comments on commit 84a1d3d

Please sign in to comment.