diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountsApiResource.java index 61cc740d4..f084e3e97 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountsApiResource.java @@ -18,6 +18,11 @@ */ package org.apache.fineract.portfolio.savings.api; +import static org.apache.fineract.infrastructure.documentmanagement.api.DocumentApiConstants.DOCUMENT_API_PARAM_DESCRIPTION; +import static org.apache.fineract.infrastructure.documentmanagement.api.DocumentApiConstants.DOCUMENT_API_PARAM_FILE; +import static org.apache.fineract.infrastructure.documentmanagement.api.DocumentApiConstants.DOCUMENT_API_PARAM_NAME; +import static org.springframework.http.HttpHeaders.CONTENT_LENGTH; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -26,6 +31,7 @@ import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; @@ -34,9 +40,11 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import java.io.InputStream; import java.time.LocalDate; import java.util.Collection; import java.util.List; @@ -50,6 +58,10 @@ import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamE import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.infrastructure.documentmanagement.api.DocumentApiResource; +import org.apache.fineract.infrastructure.documentmanagement.data.DocumentCreateResponse; +import org.apache.fineract.infrastructure.documentmanagement.data.DocumentData; +import org.apache.fineract.infrastructure.documentmanagement.data.DocumentDeleteResponse; import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.SqlValidator; @@ -63,6 +75,9 @@ import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformS import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountEditWritePlatformService; import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService; import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService; +import org.glassfish.jersey.media.multipart.FormDataBodyPart; +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataParam; import org.springframework.stereotype.Component; @Path("/v1/currentaccounts") @@ -84,6 +99,10 @@ public class CurrentAccountsApiResource { private final CurrentAccountEditWritePlatformService currentAccountEditWritePlatformService; private final FromJsonHelper fromApiJsonHelper; private final DefaultToApiJsonSerializer pendingEditApiJsonSerializer; + private final DocumentApiResource documentApiResource; + + /** Matches the {@code entityType} current-account document requests are stored/looked up under. */ + private static final String DOCUMENT_ENTITY_TYPE = "currentaccounts"; @GET @Path("template") @@ -337,4 +356,54 @@ public class CurrentAccountsApiResource { return this.toApiJsonSerializer.serialize(result); } + // ── Documents ──────────────────────────────────────────────────────────── + // The generic /v1/{entityType}/{entityId}/documents resource (DocumentApiResource) never gets a + // chance to handle current-account requests: this class's own literal /v1/currentaccounts path + // is matched first for anything under {accountId}, and it has no "documents" method, so the + // request dead-ends in a 404 instead of falling through. These delegate straight to the same + // DocumentApiResource so current accounts get identical document-management behaviour. + + @GET + @Path("{accountId}/documents") + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "List documents attached to a current account") + @ApiResponse(responseCode = "200", description = "OK") + public List retrieveDocuments(@PathParam("accountId") final Long accountId) { + return documentApiResource.retrieveAllDocuments(DOCUMENT_ENTITY_TYPE, accountId); + } + + @GET + @Path("{accountId}/documents/{documentId}/attachment") + @Produces({ MediaType.APPLICATION_OCTET_STREAM }) + @Operation(summary = "Download a document attached to a current account") + @ApiResponse(responseCode = "200", description = "Not Shown: The corresponding Binary file") + public Response downloadDocument(@PathParam("accountId") final Long accountId, @PathParam("documentId") final Long documentId) { + return documentApiResource.downloadFile(DOCUMENT_ENTITY_TYPE, accountId, documentId); + } + + @POST + @Path("{accountId}/documents") + @Consumes({ MediaType.MULTIPART_FORM_DATA }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Attach a document to a current account") + @ApiResponse(responseCode = "200", description = "Not Shown (multi-part form data)") + public DocumentCreateResponse createDocument(@PathParam("accountId") final Long accountId, + @HeaderParam(CONTENT_LENGTH) final Long fileSize, @FormDataParam(DOCUMENT_API_PARAM_FILE) final InputStream inputStream, + @FormDataParam(DOCUMENT_API_PARAM_FILE) final FormDataContentDisposition fileDetails, + @FormDataParam(DOCUMENT_API_PARAM_FILE) final FormDataBodyPart filePart, + @FormDataParam(DOCUMENT_API_PARAM_NAME) final String name, + @FormDataParam(DOCUMENT_API_PARAM_DESCRIPTION) final String description) { + return documentApiResource.createDocument(DOCUMENT_ENTITY_TYPE, accountId, fileSize, inputStream, fileDetails, filePart, name, + description); + } + + @DELETE + @Path("{accountId}/documents/{documentId}") + @Operation(summary = "Remove a document attached to a current account") + @ApiResponse(responseCode = "200", description = "OK") + public DocumentDeleteResponse deleteDocument(@PathParam("accountId") final Long accountId, + @PathParam("documentId") final Long documentId) { + return documentApiResource.deleteDocument(DOCUMENT_ENTITY_TYPE, accountId, documentId); + } + }