feat(guarantor): enrich guarantor accounts API with loan health and borrower details

- Add borrower name, savings account, guaranteed/released/remaining amounts, loan timeline, and overdue info to GuarantorAccountSummaryData
- Fix duplicate rows in GuarantorLoanAccountSummaryDataMapper by joining savings account via guarantor funding details instead of client ID
- Fix NPE in GuarantorDomainServiceImpl when loan product guarantee details are null

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-24 13:15:25 +05:30
co-authored by Claude Sonnet 4.6
parent 1eba2c50ed
commit fc80869d4b
7 changed files with 2537 additions and 2446 deletions
@@ -1,116 +1,150 @@
/**
* 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.infrastructure.core.serialization;
import com.google.gson.ExclusionStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.core.api.DateAdapter;
import org.apache.fineract.infrastructure.core.api.ExternalIdAdapter;
import org.apache.fineract.infrastructure.core.api.JodaDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.JodaMonthDayAdapter;
import org.apache.fineract.infrastructure.core.api.LocalDateAdapter;
import org.apache.fineract.infrastructure.core.api.LocalDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.LocalTimeAdapter;
import org.apache.fineract.infrastructure.core.api.OffsetDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.ParameterListExclusionStrategy;
import org.apache.fineract.infrastructure.core.api.ParameterListInclusionStrategy;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.stereotype.Service;
/**
* Helper class for serialization of Java objects into JSON using Google's GSON.
*/
@Service
public final class GoogleGsonSerializerHelper {
public Gson createGsonBuilderForPartialResponseFiltering(final Set<String> responseParameters) {
final ExclusionStrategy strategy = new ParameterListInclusionStrategy(responseParameters);
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
registerTypeAdapters(builder);
return builder.create();
}
public Gson createGsonBuilderWithParameterExclusionSerializationStrategy(final Set<String> supportedParameters,
final Set<String> responseParameters) {
final Set<String> parameterNamesToSkip = new HashSet<>();
if (!responseParameters.isEmpty()) {
// strip out all known support parameters from expected response to
// see if unsupported parameters requested for response.
final Set<String> differentParametersDetectedSet = new HashSet<>(responseParameters);
differentParametersDetectedSet.removeAll(supportedParameters);
if (!differentParametersDetectedSet.isEmpty()) {
throw new UnsupportedParameterException(new ArrayList<>(differentParametersDetectedSet));
}
parameterNamesToSkip.addAll(supportedParameters);
parameterNamesToSkip.removeAll(responseParameters);
}
final ExclusionStrategy strategy = new ParameterListExclusionStrategy(parameterNamesToSkip);
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
registerTypeAdapters(builder);
return builder.create();
}
public String serializedJsonFrom(final Gson serializer, final Object[] dataObjects) {
return serializer.toJson(dataObjects);
}
public String serializedJsonFrom(final Gson serializer, final Object singleDataObject) {
return serializer.toJson(singleDataObject);
}
public static Gson createSimpleGson() {
return createGsonBuilder().create();
}
public static GsonBuilder createGsonBuilder() {
final GsonBuilder builder = new GsonBuilder();
registerTypeAdapters(builder);
return builder;
}
public static void registerTypeAdapters(final GsonBuilder builder) {
builder.registerTypeAdapter(java.util.Date.class, new DateAdapter());
builder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
// NOTE: was missing, necessary for GSON serialization with JDK 21's restrictive module access
builder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
builder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter());
builder.registerTypeAdapter(ExternalId.class, new ExternalIdAdapter());
}
}
/**
* 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.infrastructure.core.serialization;
import com.google.gson.ExclusionStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.core.api.DateAdapter;
import org.apache.fineract.infrastructure.core.api.ExternalIdAdapter;
import org.apache.fineract.infrastructure.core.api.JodaDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.JodaMonthDayAdapter;
import org.apache.fineract.infrastructure.core.api.LocalDateAdapter;
import org.apache.fineract.infrastructure.core.api.LocalDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.LocalTimeAdapter;
import org.apache.fineract.infrastructure.core.api.OffsetDateTimeAdapter;
import org.apache.fineract.infrastructure.core.api.ParameterListExclusionStrategy;
import org.apache.fineract.infrastructure.core.api.ParameterListInclusionStrategy;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.stereotype.Service;
/**
* Helper class for serialization of Java objects into JSON using Google's GSON.
*/
@Service
public final class GoogleGsonSerializerHelper {
public Gson createGsonBuilderForPartialResponseFiltering(final Set<String> responseParameters) {
final ExclusionStrategy strategy = new ParameterListInclusionStrategy(responseParameters);
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
registerTypeAdapters(builder);
return builder.create();
}
public Gson createGsonBuilderWithParameterExclusionSerializationStrategy(final Set<String> supportedParameters,
final Set<String> responseParameters) {
final Set<String> parameterNamesToSkip = new HashSet<>();
if (!responseParameters.isEmpty()) {
// strip out all known support parameters from expected response to
// see if unsupported parameters requested for response.
final Set<String> differentParametersDetectedSet = new HashSet<>(responseParameters);
differentParametersDetectedSet.removeAll(supportedParameters);
if (!differentParametersDetectedSet.isEmpty()) {
throw new UnsupportedParameterException(new ArrayList<>(differentParametersDetectedSet));
}
parameterNamesToSkip.addAll(supportedParameters);
parameterNamesToSkip.removeAll(responseParameters);
}
final ExclusionStrategy strategy = new ParameterListExclusionStrategy(parameterNamesToSkip);
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
registerTypeAdapters(builder);
return builder.create();
}
public String serializedJsonFrom(final Gson serializer, final Object[] dataObjects) {
return serializer.toJson(dataObjects);
}
public String serializedJsonFrom(final Gson serializer, final Object singleDataObject) {
return serializer.toJson(singleDataObject);
}
public static Gson createSimpleGson() {
return createGsonBuilder().create();
}
public static GsonBuilder createGsonBuilder() {
final GsonBuilder builder = new GsonBuilder();
registerTypeAdapters(builder);
return builder;
}
public static void registerTypeAdapters(final GsonBuilder builder) {
builder.registerTypeAdapter(java.util.Date.class, new DateAdapter());
builder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
// NOTE: was missing, necessary for GSON serialization with JDK 21's restrictive module access
builder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
builder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter());
builder.registerTypeAdapter(ExternalId.class, new ExternalIdAdapter());
// Use runtime type when serializing interface-typed fields (e.g. Collection<? extends IGuarantor>)
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
if (!type.getRawType().isInterface()) {
return null;
}
final TypeAdapterFactory self = this;
return new TypeAdapter<T>() {
@Override
public void write(final JsonWriter out, final T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
TypeAdapter<T> delegate = (TypeAdapter<T>) gson.getDelegateAdapter(self,
TypeToken.get((Class<T>) value.getClass()));
delegate.write(out, value);
}
@Override
public T read(final JsonReader in) throws IOException {
return null;
}
};
}
});
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.accountdetails.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
@@ -44,12 +45,26 @@ public class GuarantorAccountSummaryData {
private final Boolean isActive;
private final String relationship;
private final BigDecimal onHoldAmount;
private final String savingsAccountNo;
private final BigDecimal amountReleased;
private final String borrowerName;
private final BigDecimal totalOverdue;
private final LocalDate overdueSinceDate;
private final LocalDate submittedOnDate;
private final LocalDate approvedOnDate;
private final LocalDate disbursedOnDate;
private final LocalDate expectedMaturityDate;
private final BigDecimal amountRemaining;
private final BigDecimal savingsBalance;
public GuarantorAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId,
final String loanProductName, final String shortLoanProductName, final LoanStatusEnumData loanStatus,
final EnumOptionData loanType, final Integer loanCycle, final Boolean inArrears, final BigDecimal originalLoan,
final BigDecimal loanBalance, final BigDecimal amountPaid, final Boolean isActive, final String relationship,
final BigDecimal onHoldAmount) {
final BigDecimal onHoldAmount, final String savingsAccountNo, final BigDecimal amountReleased,
final String borrowerName, final BigDecimal totalOverdue, final LocalDate overdueSinceDate,
final LocalDate submittedOnDate, final LocalDate approvedOnDate, final LocalDate disbursedOnDate,
final LocalDate expectedMaturityDate, final BigDecimal amountRemaining, final BigDecimal savingsBalance) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
@@ -66,5 +81,16 @@ public class GuarantorAccountSummaryData {
this.isActive = isActive;
this.relationship = relationship;
this.onHoldAmount = onHoldAmount;
this.savingsAccountNo = savingsAccountNo;
this.amountReleased = amountReleased;
this.borrowerName = borrowerName;
this.totalOverdue = totalOverdue;
this.overdueSinceDate = overdueSinceDate;
this.submittedOnDate = submittedOnDate;
this.approvedOnDate = approvedOnDate;
this.disbursedOnDate = disbursedOnDate;
this.expectedMaturityDate = expectedMaturityDate;
this.amountRemaining = amountRemaining;
this.savingsBalance = savingsBalance;
}
}
@@ -725,7 +725,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
builder.append("c.alternate_dip_verified as alternateDipVerified, ");
builder.append("c.alternate_dip_lookup_log_id as alternateDipLookupLogId, ");
builder.append("c.alternate_dip_timestamp as alternateDipTimestamp, ");
builder.append("c.dip_lookup_log_id as dipLookupLogId, ");
builder.append("c.dip_timestamp as dipTimestamp, ");
builder.append("c.name_verified as nameVerified, ");
builder.append("c.screening_log_id as screeningLogId, ");
builder.append("c.submittedon_date as submittedOnDate, ");
builder.append("sbu.username as submittedByUsername, ");
@@ -1,214 +1,215 @@
/**
* 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.loanaccount.guarantor.data;
import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import lombok.Getter;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorType;
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorEnumerations;
@Getter
public class GuarantorData implements IGuarantor {
@Serial
private static final long serialVersionUID = 1L;
private final Long id;
private final Long loanId;
private final CodeValueData clientRelationshipType;
private final EnumOptionData guarantorType;
private final String firstname;
private final String lastname;
/*** Fields for current customers/staff serving as guarantors **/
private final Long entityId;
private final String externalId;
private final String officeName;
private final LocalDate joinedDate;
/*** Fields for external persons serving as guarantors ***/
private final String addressLine1;
private final String addressLine2;
private final String city;
private final String state;
private final String zip;
private final String country;
private final String mobileNumber;
private final String housePhoneNumber;
private final String comment;
private final LocalDate dob;
private final Collection<GuarantorFundingData> guarantorFundingDetails;
private final boolean status;
// template
@SuppressWarnings("unused")
private final List<EnumOptionData> guarantorTypeOptions;
private final Collection<CodeValueData> allowedClientRelationshipTypes;
private final Collection<PortfolioAccountData> accountLinkingOptions;
// import fields
private Integer guarantorTypeId;
private Integer clientRelationshipTypeId;
private Integer savingsId;
private BigDecimal amount;
private transient Long accountId;
private transient Integer rowIndex;
private String dateFormat;
private String locale;
public static GuarantorData importInstance(Integer guarantorTypeId, Integer clientRelationshipTypeId, Long entityId, String firstname,
String lastname, String addressLine1, String addressLine2, String city, LocalDate dob, String zip, Integer savingsId,
BigDecimal amount, Integer rowIndex, Long accountId, String locale, String dateFormat) {
return new GuarantorData(guarantorTypeId, clientRelationshipTypeId, entityId, firstname, lastname, addressLine1, addressLine2, city,
dob, zip, savingsId, amount, rowIndex, accountId, locale, dateFormat);
}
private GuarantorData(Integer guarantorTypeId, Integer clientRelationshipTypeId, Long entityId, String firstname, String lastname,
String addressLine1, String addressLine2, String city, LocalDate dob, String zip, Integer savingsId, BigDecimal amount,
Integer rowIndex, Long accountId, String locale, String dateFormat) {
this.rowIndex = rowIndex;
this.firstname = firstname;
this.lastname = lastname;
this.entityId = entityId;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
this.zip = zip;
this.dob = dob;
this.guarantorTypeId = guarantorTypeId;
this.clientRelationshipTypeId = clientRelationshipTypeId;
this.savingsId = savingsId;
this.amount = amount;
this.accountId = accountId;
this.dateFormat = dateFormat;
this.locale = locale;
this.clientRelationshipType = null;
this.guarantorType = null;
this.id = null;
this.loanId = null;
this.externalId = null;
this.officeName = null;
this.joinedDate = null;
this.state = null;
this.country = null;
this.mobileNumber = null;
this.housePhoneNumber = null;
this.comment = null;
this.guarantorFundingDetails = null;
this.status = false;
this.guarantorTypeOptions = null;
this.allowedClientRelationshipTypes = null;
this.accountLinkingOptions = null;
}
public static GuarantorData template(final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, Collection<PortfolioAccountData> accountLinkingOptions) {
final Collection<GuarantorFundingData> guarantorFundingDetails = null;
final boolean status = false;
return new GuarantorData(null, null, null, null, GuarantorEnumerations.guarantorType(GuarantorType.CUSTOMER), null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, status, guarantorFundingDetails,
guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions);
}
public static GuarantorData templateOnTop(final GuarantorData guarantorData, final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, Collection<PortfolioAccountData> accountLinkingOptions) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, guarantorData.firstname, guarantorData.lastname, guarantorData.dob, guarantorData.addressLine1,
guarantorData.addressLine2, guarantorData.city, guarantorData.state, guarantorData.zip, guarantorData.country,
guarantorData.mobileNumber, guarantorData.housePhoneNumber, guarantorData.comment, guarantorData.officeName,
guarantorData.joinedDate, guarantorData.externalId, guarantorData.status, guarantorData.guarantorFundingDetails,
guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions);
}
public static GuarantorData mergeClientData(final ClientData clientData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, clientData.getFirstname(), clientData.getLastname(), null, null, null, null, null, null, null,
null, null, null, clientData.getOfficeName(), clientData.getActivationDate(), clientData.getExternalId().getValue(),
guarantorData.status, guarantorData.guarantorFundingDetails, null, guarantorData.allowedClientRelationshipTypes,
guarantorData.accountLinkingOptions);
}
public static GuarantorData mergeStaffData(final StaffData staffData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, staffData.getFirstname(), staffData.getLastname(), null, null, null, null, null, null, null,
null, null, null, staffData.getOfficeName(), null, null, guarantorData.status, guarantorData.guarantorFundingDetails, null,
guarantorData.allowedClientRelationshipTypes, guarantorData.accountLinkingOptions);
}
public GuarantorData(final Long id, final Long loanId, final CodeValueData clientRelationshipType, final Long entityId,
final EnumOptionData guarantorType, final String firstname, final String lastname, final LocalDate dob,
final String addressLine1, final String addressLine2, final String city, final String state, final String zip,
final String country, final String mobileNumber, final String housePhoneNumber, final String comment, final String officeName,
final LocalDate joinedDate, final String externalId, final boolean status,
Collection<GuarantorFundingData> guarantorFundingDetails, final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, final Collection<PortfolioAccountData> accountLinkingOptions) {
this.id = id;
this.loanId = loanId;
this.clientRelationshipType = clientRelationshipType;
this.guarantorType = guarantorType;
this.entityId = entityId;
this.firstname = firstname;
this.lastname = lastname;
this.dob = dob;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
this.state = state;
this.zip = zip;
this.country = country;
this.mobileNumber = mobileNumber;
this.housePhoneNumber = housePhoneNumber;
this.comment = comment;
this.officeName = officeName;
this.joinedDate = joinedDate;
this.externalId = externalId;
this.status = status;
this.guarantorFundingDetails = guarantorFundingDetails;
this.guarantorTypeOptions = guarantorTypeOptions;
this.allowedClientRelationshipTypes = allowedClientRelationshipTypes;
this.accountLinkingOptions = accountLinkingOptions;
}
public boolean isExternalGuarantor() {
return GuarantorType.EXTERNAL.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isExistingClient() {
return GuarantorType.CUSTOMER.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isStaffMember() {
return GuarantorType.STAFF.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isExistingGroup() {
return GuarantorType.GROUP.getValue().equals(this.guarantorType.getId().intValue());
}
}
/**
* 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.loanaccount.guarantor.data;
import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import lombok.Getter;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorType;
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorEnumerations;
@Getter
public class GuarantorData implements IGuarantor {
@Serial
private static final long serialVersionUID = 1L;
private final Long id;
private final Long loanId;
private final CodeValueData clientRelationshipType;
private final EnumOptionData guarantorType;
private final String firstname;
private final String lastname;
/*** Fields for current customers/staff serving as guarantors **/
private final Long entityId;
private final String externalId;
private final String officeName;
private final LocalDate joinedDate;
/*** Fields for external persons serving as guarantors ***/
private final String addressLine1;
private final String addressLine2;
private final String city;
private final String state;
private final String zip;
private final String country;
private final String mobileNumber;
private final String housePhoneNumber;
private final String comment;
private final LocalDate dob;
private final Collection<GuarantorFundingData> guarantorFundingDetails;
private final boolean status;
// template
@SuppressWarnings("unused")
private final List<EnumOptionData> guarantorTypeOptions;
private final Collection<CodeValueData> allowedClientRelationshipTypes;
private final Collection<PortfolioAccountData> accountLinkingOptions;
// import fields
private Integer guarantorTypeId;
private Integer clientRelationshipTypeId;
private Integer savingsId;
private BigDecimal amount;
private transient Long accountId;
private transient Integer rowIndex;
private String dateFormat;
private String locale;
public static GuarantorData importInstance(Integer guarantorTypeId, Integer clientRelationshipTypeId, Long entityId, String firstname,
String lastname, String addressLine1, String addressLine2, String city, LocalDate dob, String zip, Integer savingsId,
BigDecimal amount, Integer rowIndex, Long accountId, String locale, String dateFormat) {
return new GuarantorData(guarantorTypeId, clientRelationshipTypeId, entityId, firstname, lastname, addressLine1, addressLine2, city,
dob, zip, savingsId, amount, rowIndex, accountId, locale, dateFormat);
}
private GuarantorData(Integer guarantorTypeId, Integer clientRelationshipTypeId, Long entityId, String firstname, String lastname,
String addressLine1, String addressLine2, String city, LocalDate dob, String zip, Integer savingsId, BigDecimal amount,
Integer rowIndex, Long accountId, String locale, String dateFormat) {
this.rowIndex = rowIndex;
this.firstname = firstname;
this.lastname = lastname;
this.entityId = entityId;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
this.zip = zip;
this.dob = dob;
this.guarantorTypeId = guarantorTypeId;
this.clientRelationshipTypeId = clientRelationshipTypeId;
this.savingsId = savingsId;
this.amount = amount;
this.accountId = accountId;
this.dateFormat = dateFormat;
this.locale = locale;
this.clientRelationshipType = null;
this.guarantorType = null;
this.id = null;
this.loanId = null;
this.externalId = null;
this.officeName = null;
this.joinedDate = null;
this.state = null;
this.country = null;
this.mobileNumber = null;
this.housePhoneNumber = null;
this.comment = null;
this.guarantorFundingDetails = null;
this.status = false;
this.guarantorTypeOptions = null;
this.allowedClientRelationshipTypes = null;
this.accountLinkingOptions = null;
}
public static GuarantorData template(final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, Collection<PortfolioAccountData> accountLinkingOptions) {
final Collection<GuarantorFundingData> guarantorFundingDetails = null;
final boolean status = false;
return new GuarantorData(null, null, null, null, GuarantorEnumerations.guarantorType(GuarantorType.CUSTOMER), null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, status, guarantorFundingDetails,
guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions);
}
public static GuarantorData templateOnTop(final GuarantorData guarantorData, final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, Collection<PortfolioAccountData> accountLinkingOptions) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, guarantorData.firstname, guarantorData.lastname, guarantorData.dob, guarantorData.addressLine1,
guarantorData.addressLine2, guarantorData.city, guarantorData.state, guarantorData.zip, guarantorData.country,
guarantorData.mobileNumber, guarantorData.housePhoneNumber, guarantorData.comment, guarantorData.officeName,
guarantorData.joinedDate, guarantorData.externalId, guarantorData.status, guarantorData.guarantorFundingDetails,
guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions);
}
public static GuarantorData mergeClientData(final ClientData clientData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, clientData.getFirstname(), clientData.getLastname(), null, null, null, null, null, null, null,
null, null, null, clientData.getOfficeName(), clientData.getActivationDate(),
clientData.getExternalId() != null ? clientData.getExternalId().getValue() : null,
guarantorData.status, guarantorData.guarantorFundingDetails, null, guarantorData.allowedClientRelationshipTypes,
guarantorData.accountLinkingOptions);
}
public static GuarantorData mergeStaffData(final StaffData staffData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, staffData.getFirstname(), staffData.getLastname(), null, null, null, null, null, null, null,
null, null, null, staffData.getOfficeName(), null, null, guarantorData.status, guarantorData.guarantorFundingDetails, null,
guarantorData.allowedClientRelationshipTypes, guarantorData.accountLinkingOptions);
}
public GuarantorData(final Long id, final Long loanId, final CodeValueData clientRelationshipType, final Long entityId,
final EnumOptionData guarantorType, final String firstname, final String lastname, final LocalDate dob,
final String addressLine1, final String addressLine2, final String city, final String state, final String zip,
final String country, final String mobileNumber, final String housePhoneNumber, final String comment, final String officeName,
final LocalDate joinedDate, final String externalId, final boolean status,
Collection<GuarantorFundingData> guarantorFundingDetails, final List<EnumOptionData> guarantorTypeOptions,
final Collection<CodeValueData> allowedClientRelationshipTypes, final Collection<PortfolioAccountData> accountLinkingOptions) {
this.id = id;
this.loanId = loanId;
this.clientRelationshipType = clientRelationshipType;
this.guarantorType = guarantorType;
this.entityId = entityId;
this.firstname = firstname;
this.lastname = lastname;
this.dob = dob;
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
this.state = state;
this.zip = zip;
this.country = country;
this.mobileNumber = mobileNumber;
this.housePhoneNumber = housePhoneNumber;
this.comment = comment;
this.officeName = officeName;
this.joinedDate = joinedDate;
this.externalId = externalId;
this.status = status;
this.guarantorFundingDetails = guarantorFundingDetails;
this.guarantorTypeOptions = guarantorTypeOptions;
this.allowedClientRelationshipTypes = allowedClientRelationshipTypes;
this.accountLinkingOptions = accountLinkingOptions;
}
public boolean isExternalGuarantor() {
return GuarantorType.EXTERNAL.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isExistingClient() {
return GuarantorType.CUSTOMER.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isStaffMember() {
return GuarantorType.STAFF.getValue().equals(this.guarantorType.getId().intValue());
}
public boolean isExistingGroup() {
return GuarantorType.GROUP.getValue().equals(this.guarantorType.getId().intValue());
}
}