diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 2ef93add68e..2d9a7f05be2 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -382,14 +382,6 @@ public CommandWrapperBuilder updateClientAddress(final long clientId) { return this; } - public CommandWrapperBuilder addFamilyMembers(final long clientId) { - this.actionName = ACTION_CREATE; - this.entityName = ENTITY_FAMILYMEMBERS; - this.href = "/clients/" + clientId + "/familymembers"; - this.clientId = clientId; - return this; - } - public CommandWrapperBuilder updateFamilyMembers(final long familyMemberId) { this.actionName = ACTION_UPDATE; this.entityName = ENTITY_FAMILYMEMBERS; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientFamilyMembersApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientFamilyMembersApiResource.java index 5ef45336771..d2fcb29bfe1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientFamilyMembersApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientFamilyMembersApiResource.java @@ -21,7 +21,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -32,15 +34,20 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import java.util.List; +import java.util.function.Supplier; import lombok.RequiredArgsConstructor; +import org.apache.fineract.command.core.CommandDispatcher; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.client.command.FamilyMemberCreateCommand; import org.apache.fineract.portfolio.client.data.ClientFamilyMemberRequest; import org.apache.fineract.portfolio.client.data.ClientFamilyMembersData; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateResponse; import org.apache.fineract.portfolio.client.service.ClientFamilyMembersReadPlatformService; import org.springframework.stereotype.Component; @@ -51,10 +58,12 @@ public class ClientFamilyMembersApiResource { private static final String RESOURCE_NAME_FOR_PERMISSIONS = "FamilyMembers"; + private final PlatformSecurityContext context; private final ClientFamilyMembersReadPlatformService readPlatformService; private final ToApiJsonSerializer toApiJsonSerializer; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final CommandDispatcher dispatcher; @GET @Path("/{familyMemberId}") @@ -62,9 +71,7 @@ public class ClientFamilyMembersApiResource { @Operation(summary = "Retrieve a client family member", operationId = "retrieveOneClientFamilyMember") public ClientFamilyMembersData getFamilyMember(@PathParam("familyMemberId") final Long familyMemberId, @PathParam("clientId") @Parameter(description = "clientId") final Long clientId) { - this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); - return this.readPlatformService.getClientFamilyMember(clientId, familyMemberId); } @@ -95,7 +102,6 @@ public CommandProcessingResult updateClientFamilyMembers(@PathParam("familyMembe @PathParam("clientId") @Parameter(description = "clientId") final Long clientId) { final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFamilyMembers(familyMemberId) .withJson(toApiJsonSerializer.serialize(clientFamilyMemberRequest)).build(); - return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } @@ -103,12 +109,14 @@ public CommandProcessingResult updateClientFamilyMembers(@PathParam("familyMembe @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Add a client family member", operationId = "createClientFamilyMember") - public CommandProcessingResult addClientFamilyMembers(@PathParam("clientId") final long clientid, - ClientFamilyMemberRequest clientFamilyMemberRequest) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().addFamilyMembers(clientid) - .withJson(toApiJsonSerializer.serialize(clientFamilyMemberRequest)).build(); - - return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + public FamilyMemberCreateResponse addClientFamilyMembers( + @PathParam("clientId") @Parameter(description = "clientId") final Long clientId, + @RequestBody(required = true) @Valid FamilyMemberCreateRequest request) { + request.setClientId(clientId); + final var command = new FamilyMemberCreateCommand(); + command.setPayload(request); + final Supplier response = dispatcher.dispatch(command); + return response.get(); } @DELETE @@ -118,8 +126,6 @@ public CommandProcessingResult addClientFamilyMembers(@PathParam("clientId") fin public CommandProcessingResult deleteClientFamilyMembers(@PathParam("familyMemberId") final long familyMemberId, @PathParam("clientId") @Parameter(description = "clientId") final Long clientId) { final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteFamilyMembers(familyMemberId).build(); - return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/command/FamilyMemberCreateCommand.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/command/FamilyMemberCreateCommand.java new file mode 100644 index 00000000000..fa8fde0585b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/command/FamilyMemberCreateCommand.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.command; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.fineract.command.core.Command; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; + +@Data +@EqualsAndHashCode(callSuper = true) +public class FamilyMemberCreateCommand extends Command {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateRequest.java new file mode 100644 index 00000000000..6dd53694e43 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateRequest.java @@ -0,0 +1,88 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.data; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.Hidden; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Past; +import jakarta.validation.constraints.Positive; +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FamilyMemberCreateRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Hidden + private Long clientId; + + @NotNull(message = "{org.apache.fineract.portfolio.client.familymember.firstname.not-null}") + @Length(max = 100, message = "{org.apache.fineract.portfolio.client.familymember.firstname.max}") + private String firstName; + + @Length(max = 100, message = "{org.apache.fineract.portfolio.client.familymember.middlename.max}") + private String middleName; + + @NotNull(message = "{org.apache.fineract.portfolio.client.familymember.lastname.not-null}") + @Length(max = 100, message = "{org.apache.fineract.portfolio.client.familymember.lastname.max}") + private String lastName; + + @Length(max = 100, message = "{org.apache.fineract.portfolio.client.familymember.qualification.max}") + private String qualification; + + @Length(max = 100, message = "{org.apache.fineract.portfolio.client.familymember.mobile-number.max}") + private String mobileNumber; + + @Positive(message = "{org.apache.fineract.portfolio.client.familymember.age.positive}") + private Long age; + + @JsonProperty("isDependent") + private Boolean isDependent; + + @NotNull(message = "{org.apache.fineract.portfolio.client.familymember.relationship-id.not-null}") + @Positive(message = "{org.apache.fineract.portfolio.client.familymember.relationship-id.positive}") + private Long relationshipId; + + @Positive(message = "{org.apache.fineract.portfolio.client.familymember.marital-status-id.positive}") + private Long maritalStatusId; + + @Positive(message = "{org.apache.fineract.portfolio.client.familymember.gender-id.positive}") + private Long genderId; + + @Positive(message = "{org.apache.fineract.portfolio.client.familymember.profession-id.positive}") + private Long professionId; + + @Past(message = "{org.apache.fineract.portfolio.client.familymember.date-of-birth.past}") + private LocalDate dateOfBirth; + + private String locale; + private String dateFormat; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateResponse.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateResponse.java new file mode 100644 index 00000000000..6fd32480147 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/FamilyMemberCreateResponse.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.data; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FamilyMemberCreateResponse { + + private Long clientId; + private Long resourceId; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java index 5517b42a529..d51e47eadbe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - package org.apache.fineract.portfolio.client.domain; import jakarta.persistence.Column; @@ -25,9 +24,13 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import java.time.LocalDate; +import lombok.Getter; +import lombok.Setter; import org.apache.fineract.infrastructure.codes.domain.CodeValue; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +@Getter +@Setter @Entity @Table(name = "m_family_members") public class ClientFamilyMembers extends AbstractPersistableCustom { @@ -76,138 +79,23 @@ public class ClientFamilyMembers extends AbstractPersistableCustom { @Column(name = "date_of_birth", nullable = true) private LocalDate dateOfBirth; - private ClientFamilyMembers(final Client client, final String firstName, final String middleName, final String lastName, - final String qualification, final String mobileNumber, final Long age, final Boolean isDependent, final CodeValue relationship, - final CodeValue maritalStatus, final CodeValue gender, final LocalDate dateOfBirth, final CodeValue profession) { - - this.client = client; - this.firstName = firstName; - this.middleName = middleName; - this.lastName = lastName; - this.qualification = qualification; - this.age = age; - this.mobileNumber = mobileNumber; - this.isDependent = isDependent; - this.relationship = relationship; - this.maritalStatus = maritalStatus; - this.gender = gender; - this.dateOfBirth = dateOfBirth; - this.profession = profession; - } - - public ClientFamilyMembers() { - - } - public static ClientFamilyMembers fromJson(final Client client, final String firstName, final String middleName, final String lastName, final String qualification, final String mobileNumber, final Long age, final Boolean isDependent, final CodeValue relationship, final CodeValue maritalStatus, final CodeValue gender, final LocalDate dateOfBirth, final CodeValue profession) { - return new ClientFamilyMembers(client, firstName, middleName, lastName, qualification, mobileNumber, age, isDependent, relationship, - maritalStatus, gender, dateOfBirth, profession); - } - - public Client getClient() { - return this.client; - } - - public void setClient(Client client) { - this.client = client; - } - - public String getFirstName() { - return this.firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getMiddleName() { - return this.middleName; - } - - public void setMiddleName(String middleName) { - this.middleName = middleName; - } - - public String getLastName() { - return this.lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getQualification() { - return this.qualification; - } - - public void setQualification(String qualification) { - this.qualification = qualification; - } - - public CodeValue getRelationship() { - return this.relationship; + ClientFamilyMembers entity = new ClientFamilyMembers(); + entity.setClient(client); + entity.setFirstName(firstName); + entity.setMiddleName(middleName); + entity.setLastName(lastName); + entity.setQualification(qualification); + entity.setMobileNumber(mobileNumber); + entity.setAge(age); + entity.setIsDependent(isDependent); + entity.setRelationship(relationship); + entity.setMaritalStatus(maritalStatus); + entity.setGender(gender); + entity.setDateOfBirth(dateOfBirth); + entity.setProfession(profession); + return entity; } - - public void setRelationship(CodeValue relationship) { - this.relationship = relationship; - } - - public CodeValue getMaritalStatus() { - return this.maritalStatus; - } - - public void setMaritalStatus(CodeValue maritalStatus) { - this.maritalStatus = maritalStatus; - } - - public CodeValue getGender() { - return this.gender; - } - - public void setGender(CodeValue gender) { - this.gender = gender; - } - - public CodeValue getProfession() { - return this.profession; - } - - public void setProfession(CodeValue profession) { - this.profession = profession; - } - - public LocalDate getDateOfBirth() { - return this.dateOfBirth; - } - - public void setDateOfBirth(LocalDate dateOfBirth) { - this.dateOfBirth = dateOfBirth; - } - - public String getMobileNumber() { - return this.mobileNumber; - } - - public void setMobileNumber(String mobileNumber) { - this.mobileNumber = mobileNumber; - } - - public Long getAge() { - return this.age; - } - - public void setAge(Long age) { - this.age = age; - } - - public Boolean getIsDependent() { - return this.isDependent; - } - - public void setIsDependent(Boolean isDependent) { - this.isDependent = isDependent; - } - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/AddClientFamilyMemberCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/AddClientFamilyMemberCommandHandler.java deleted file mode 100644 index 2677e72002b..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/AddClientFamilyMemberCommandHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fineract.portfolio.client.handler; - -import lombok.RequiredArgsConstructor; -import org.apache.fineract.commands.annotation.CommandType; -import org.apache.fineract.commands.handler.NewCommandSourceHandler; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.portfolio.client.service.ClientFamilyMembersWritePlatformService; -import org.springframework.stereotype.Service; - -@Service -@CommandType(entity = "FAMILYMEMBERS", action = "CREATE") -@RequiredArgsConstructor -public class AddClientFamilyMemberCommandHandler implements NewCommandSourceHandler { - - private final ClientFamilyMembersWritePlatformService clientFamilyMembersWritePlatformService; - - @Override - public CommandProcessingResult processCommand(JsonCommand command) { - - return this.clientFamilyMembersWritePlatformService.addFamilyMember(command.getClientId(), command); - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/FamilyMemberCreateCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/FamilyMemberCreateCommandHandler.java new file mode 100644 index 00000000000..a2493d1efd0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/handler/FamilyMemberCreateCommandHandler.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.handler; + +import io.github.resilience4j.retry.annotation.Retry; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.command.core.Command; +import org.apache.fineract.command.core.CommandHandler; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateResponse; +import org.apache.fineract.portfolio.client.service.FamilyMemberWriteService; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FamilyMemberCreateCommandHandler implements CommandHandler { + + private final FamilyMemberWriteService writeService; + + @Retry(name = "commandFamilyMemberCreate", fallbackMethod = "fallback") + @Override + @Transactional + public FamilyMemberCreateResponse handle(Command command) { + return writeService.createFamilyMember(command.getPayload()); + } + + @Override + public FamilyMemberCreateResponse fallback(Command command, Throwable t) { + return CommandHandler.super.fallback(command, t); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/FamilyMemberCreateRequestMapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/FamilyMemberCreateRequestMapper.java new file mode 100644 index 00000000000..bf31404cf0f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/FamilyMemberCreateRequestMapper.java @@ -0,0 +1,37 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.mapper; + +import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; +import org.apache.fineract.portfolio.client.domain.ClientFamilyMembers; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = MapstructMapperConfig.class) +public abstract class FamilyMemberCreateRequestMapper { + + @Mapping(ignore = true, target = "id") + @Mapping(ignore = true, target = "client") + @Mapping(ignore = true, target = "relationship") + @Mapping(ignore = true, target = "maritalStatus") + @Mapping(ignore = true, target = "gender") + @Mapping(ignore = true, target = "profession") + public abstract ClientFamilyMembers map(FamilyMemberCreateRequest source); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformService.java index 9b7dd7edbb3..fcd334008e1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformService.java @@ -25,8 +25,6 @@ public interface ClientFamilyMembersWritePlatformService { - CommandProcessingResult addFamilyMember(long clientId, JsonCommand command); - CommandProcessingResult addClientFamilyMember(Client client, JsonCommand command); CommandProcessingResult updateFamilyMember(Long familyMemberId, JsonCommand command); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java index 4400f4c3d80..39696e125a4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java @@ -64,72 +64,6 @@ public ClientFamilyMembersWritePlatformServiceImpl(final PlatformSecurityContext } - @Override - public CommandProcessingResult addFamilyMember(final long clientId, final JsonCommand command) { - - Long relationshipId = null; - CodeValue relationship = null; - CodeValue maritalStatus = null; - Long maritalStatusId = null; - Long genderId = null; - CodeValue gender = null; - Long professionId = null; - CodeValue profession = null; - String firstName = ""; - String middleName = ""; - String lastName = ""; - String qualification = ""; - String mobileNumber = ""; - Long age = null; - Boolean isDependent = false; - LocalDate dateOfBirth = null; - - this.context.authenticatedUser(); - apiJsonDeserializer.validateForCreate(clientId, command.json()); - - Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); - firstName = command.stringValueOfParameterNamed("firstName"); - middleName = command.stringValueOfParameterNamed("middleName"); - lastName = command.stringValueOfParameterNamed("lastName"); - qualification = command.stringValueOfParameterNamed("qualification"); - mobileNumber = command.stringValueOfParameterNamed("mobileNumber"); - age = command.longValueOfParameterNamed("age"); - isDependent = command.booleanObjectValueOfParameterNamed("isDependent"); - - if (command.longValueOfParameterNamed("relationshipId") != null) { - relationshipId = command.longValueOfParameterNamed("relationshipId"); - relationship = this.codeValueRepository.getReferenceById(relationshipId); - } - - if (command.longValueOfParameterNamed("maritalStatusId") != null) { - maritalStatusId = command.longValueOfParameterNamed("maritalStatusId"); - maritalStatus = this.codeValueRepository.getReferenceById(maritalStatusId); - } - - if (command.longValueOfParameterNamed("genderId") != null) { - genderId = command.longValueOfParameterNamed("genderId"); - gender = this.codeValueRepository.getReferenceById(genderId); - } - - if (command.longValueOfParameterNamed("professionId") != null) { - professionId = command.longValueOfParameterNamed("professionId"); - profession = this.codeValueRepository.getReferenceById(professionId); - } - - dateOfBirth = command.localDateValueOfParameterNamed("dateOfBirth"); - - ClientFamilyMembers clientFamilyMembers = ClientFamilyMembers.fromJson(client, firstName, middleName, lastName, qualification, - mobileNumber, age, isDependent, relationship, maritalStatus, gender, dateOfBirth, profession); - - this.clientFamilyRepository.saveAndFlush(clientFamilyMembers); - - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(clientFamilyMembers.getId()) // - .build(); - - } - @Override public CommandProcessingResult addClientFamilyMember(final Client client, final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteService.java new file mode 100644 index 00000000000..407edc88063 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteService.java @@ -0,0 +1,27 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.service; + +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateResponse; + +public interface FamilyMemberWriteService { + + FamilyMemberCreateResponse createFamilyMember(FamilyMemberCreateRequest request); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteServiceImpl.java new file mode 100644 index 00000000000..794e110c94d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/FamilyMemberWriteServiceImpl.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.service; + +import jakarta.persistence.PersistenceException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; +import org.apache.fineract.infrastructure.core.exception.ErrorHandler; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateRequest; +import org.apache.fineract.portfolio.client.data.FamilyMemberCreateResponse; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; +import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; +import org.apache.fineract.portfolio.client.mapper.FamilyMemberCreateRequestMapper; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +public class FamilyMemberWriteServiceImpl implements FamilyMemberWriteService { + + private final ClientRepositoryWrapper clientRepositoryWrapper; + private final ClientFamilyMembersRepository clientFamilyRepository; + private final CodeValueRepository codeValueRepository; + private final FamilyMemberCreateRequestMapper familyMemberCreateRequestMapper; + + @Transactional + @Override + public FamilyMemberCreateResponse createFamilyMember(final FamilyMemberCreateRequest request) { + try { + var entity = familyMemberCreateRequestMapper.map(request); + + Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(request.getClientId()); + entity.setClient(client); + + entity.setRelationship(codeValueRepository.getReferenceById(request.getRelationshipId())); + + if (request.getMaritalStatusId() != null) { + entity.setMaritalStatus(codeValueRepository.getReferenceById(request.getMaritalStatusId())); + } + if (request.getGenderId() != null) { + entity.setGender(codeValueRepository.getReferenceById(request.getGenderId())); + } + if (request.getProfessionId() != null) { + entity.setProfession(codeValueRepository.getReferenceById(request.getProfessionId())); + } + + clientFamilyRepository.saveAndFlush(entity); + + return FamilyMemberCreateResponse.builder().resourceId(entity.getId()).clientId(request.getClientId()).build(); + } catch (final JpaSystemException | DataIntegrityViolationException dve) { + throw handleDataIntegrityIssues(dve.getMostSpecificCause(), dve); + } catch (final PersistenceException dve) { + var throwable = ExceptionUtils.getRootCause(dve.getCause()); + throw handleDataIntegrityIssues(throwable, dve); + } + } + + private RuntimeException handleDataIntegrityIssues(final Throwable realCause, final Exception dve) { + log.error("Error occurred.", dve); + return ErrorHandler.getMappable(dve, "error.msg.familymember.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/starter/FamilyMemberConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/starter/FamilyMemberConfiguration.java new file mode 100644 index 00000000000..450fb0cc605 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/starter/FamilyMemberConfiguration.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.client.starter; + +import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; +import org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; +import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; +import org.apache.fineract.portfolio.client.mapper.FamilyMemberCreateRequestMapper; +import org.apache.fineract.portfolio.client.service.FamilyMemberWriteService; +import org.apache.fineract.portfolio.client.service.FamilyMemberWriteServiceImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class FamilyMemberConfiguration { + + @Bean + @ConditionalOnMissingBean(FamilyMemberWriteService.class) + FamilyMemberWriteService familyMemberWriteService(ClientRepositoryWrapper clientRepositoryWrapper, + ClientFamilyMembersRepository clientFamilyRepository, CodeValueRepository codeValueRepository, + FamilyMemberCreateRequestMapper familyMemberCreateRequestMapper) { + return new FamilyMemberWriteServiceImpl(clientRepositoryWrapper, clientFamilyRepository, codeValueRepository, + familyMemberCreateRequestMapper); + } +} diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index d8eed37f191..d47567f5694 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -714,6 +714,11 @@ resilience4j.retry.instances.commandStaffCreate.wait-duration=${FINERACT_COMMAND resilience4j.retry.instances.commandStaffCreate.enable-exponential-backoff=${FINERACT_COMMAND_STAFF_CREATE_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true} resilience4j.retry.instances.commandStaffCreate.exponential-backoff-multiplier=${FINERACT_COMMAND_STAFF_CREATE_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2} resilience4j.retry.instances.commandStaffCreate.retryExceptions=${FINERACT_COMMAND_STAFF_CREATE_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException} +resilience4j.retry.instances.commandFamilyMemberCreate.max-attempts=${FINERACT_COMMAND_FAMILY_MEMBER_CREATE_RETRY_MAX_ATTEMPTS:3} +resilience4j.retry.instances.commandFamilyMemberCreate.wait-duration=${FINERACT_COMMAND_FAMILY_MEMBER_CREATE_RETRY_WAIT_DURATION:1s} +resilience4j.retry.instances.commandFamilyMemberCreate.enable-exponential-backoff=${FINERACT_COMMAND_FAMILY_MEMBER_CREATE_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true} +resilience4j.retry.instances.commandFamilyMemberCreate.exponential-backoff-multiplier=${FINERACT_COMMAND_FAMILY_MEMBER_CREATE_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2} +resilience4j.retry.instances.commandFamilyMemberCreate.retryExceptions=${FINERACT_COMMAND_FAMILY_MEMBER_CREATE_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException} resilience4j.retry.instances.commandStaffUpdate.max-attempts=${FINERACT_COMMAND_STAFF_UPDATE_RETRY_MAX_ATTEMPTS:3} resilience4j.retry.instances.commandStaffUpdate.wait-duration=${FINERACT_COMMAND_STAFF_UPDATE_RETRY_WAIT_DURATION:1s} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientFamilyMemberTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientFamilyMemberTest.java index 176050b0297..017f3b3fc69 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientFamilyMemberTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientFamilyMemberTest.java @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.apache.fineract.client.models.ClientFamilyMemberRequest; +import org.apache.fineract.client.models.FamilyMemberCreateRequest; import org.apache.fineract.client.models.PostClientsRequest; import org.apache.fineract.client.models.PostCodeValuesDataRequest; import org.apache.fineract.client.services.ClientFamilyMemberApi; @@ -58,7 +58,7 @@ public void retrieveFamilyMemberReturns404ForAnotherClientsMember() { new PostCodeValuesDataRequest().name(Utils.randomStringGenerator("Relative_", 4)).position(1).isActive(true))) .getSubResourceId(); Long familyMemberId = ok(familyMemberApi.createClientFamilyMember(ownerClientId, - new ClientFamilyMemberRequest().firstName("Ada").lastName("Lovelace").relationshipId(relationshipId))).getResourceId(); + new FamilyMemberCreateRequest().firstName("Ada").lastName("Lovelace").relationshipId(relationshipId))).getResourceId(); // sanity: reachable through the owning client assertEquals(familyMemberId, ok(familyMemberApi.retrieveOneClientFamilyMember(familyMemberId, ownerClientId)).getId());