Merge pull request #503 from johnw65/externalIds

mifosx-567 add support for externalId for loan transactions
This commit is contained in:
John Woodlock
2013-08-02 12:09:51 -07:00
8 changed files with 64 additions and 43 deletions
@@ -0,0 +1,3 @@
ALTER TABLE m_loan_transaction
ADD COLUMN `external_id` VARCHAR(100) NULL DEFAULT NULL AFTER `is_reversed` ,
ADD UNIQUE INDEX `external_id_UNIQUE` (`external_id` ASC) ;
@@ -45,7 +45,7 @@ import org.springframework.stereotype.Component;
@Scope("singleton")
public class LoanTransactionsApiResource {
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "type", "date", "currency", "amount"));
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId"));
private final String resourceNameForPermissions = "LOAN";
@@ -32,6 +32,7 @@ public class LoanTransactionData {
private final BigDecimal interestPortion;
private final BigDecimal feeChargesPortion;
private final BigDecimal penaltyChargesPortion;
private final String externalId;
// templates
final Collection<CodeValueData> paymentTypeOptions;
@@ -40,20 +41,20 @@ public class LoanTransactionData {
return new LoanTransactionData(loanTransactionData.id, loanTransactionData.type, loanTransactionData.paymentDetailData,
loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion,
loanTransactionData.interestPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion,
paymentTypeOptions);
paymentTypeOptions, loanTransactionData.externalId);
}
public LoanTransactionData(final Long id, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData,
final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion) {
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final String externalId) {
this(id, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, null);
penaltyChargesPortion, null, externalId);
}
public LoanTransactionData(final Long id, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData,
final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion,
final Collection<CodeValueData> paymentTypeOptions) {
final Collection<CodeValueData> paymentTypeOptions, final String externalId) {
this.id = id;
this.type = transactionType;
this.paymentDetailData = paymentDetailData;
@@ -65,6 +66,7 @@ public class LoanTransactionData {
this.feeChargesPortion = feeChargesPortion;
this.penaltyChargesPortion = penaltyChargesPortion;
this.paymentTypeOptions = paymentTypeOptions;
this.externalId = externalId;
}
public LocalDate dateOf() {
@@ -1331,7 +1331,8 @@ public class Loan extends AbstractPersistable<Long> {
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final String txnExternalId = command.stringValueOfParameterNamed("externalId");
this.actualDisbursementDate = actualDisbursementDate.toDate();
this.disbursedBy = currentUser;
updateLoanScheduleDependentDerivedFields();
@@ -1344,7 +1345,7 @@ public class Loan extends AbstractPersistable<Long> {
validateDisbursementDateIsOnNonWorkingDay(workingDays);
validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays);
handleDisbursementTransaction(paymentDetail, actualDisbursementDate);
handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId);
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
@@ -1394,10 +1395,10 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSchedule(loanSchedule);
}
private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn) {
private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn, final String txnExternalId) {
// track disbursement transaction
final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(),
paymentDetail, disbursedOn);
paymentDetail, disbursedOn, txnExternalId);
disbursementTransaction.updateLoan(this);
this.loanTransactions.add(disbursementTransaction);
@@ -1411,7 +1412,7 @@ public class Loan extends AbstractPersistable<Long> {
final Money totalFeeChargesDueAtDisbursement = this.summary.getTotalFeeChargesDueAtDisbursement(loanCurrency());
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()) {
LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn);
LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn, txnExternalId);
Money zero = Money.zero(getCurrency());
chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero);
chargesPayment.updateLoan(this);
@@ -1830,6 +1831,7 @@ public class Loan extends AbstractPersistable<Long> {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate");
final String txnExternalId = command.stringValueOfParameterNamed("externalId");
this.closedOnDate = writtenOffOnLocalDate.toDate();
this.writtenOffOnDate = writtenOffOnLocalDate.toDate();
@@ -1849,7 +1851,7 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate);
}
loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate);
loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate, txnExternalId);
final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions);
if (!isLastTransaction) {
final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
@@ -1876,7 +1878,8 @@ public class Loan extends AbstractPersistable<Long> {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate");
final String txnExternalId = command.stringValueOfParameterNamed("externalId");
this.closedOnDate = closureDate.toDate();
changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate"));
@@ -1905,7 +1908,7 @@ public class Loan extends AbstractPersistable<Long> {
}
this.closedOnDate = closureDate.toDate();
loanTransaction = LoanTransaction.writeoff(this, closureDate);
loanTransaction = LoanTransaction.writeoff(this, closureDate, txnExternalId);
boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions);
if (!isLastTransaction) {
final String errorMessage = "The closing date of the loan must be on or after latest transaction date.";
@@ -23,6 +23,7 @@ import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
@@ -42,7 +43,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
* Disbursements, Repayments, Waivers, Write-off etc
*/
@Entity
@Table(name = "m_loan_transaction")
@Table(name = "m_loan_transaction", uniqueConstraints = {@UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") })
public final class LoanTransaction extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@@ -78,6 +79,9 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
@Column(name = "is_reversed", nullable = false)
private boolean reversed;
@Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true)
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<LoanChargePaidBy>();
@@ -88,24 +92,24 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
this.typeOf = null;
}
public static LoanTransaction disbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate disbursementDate) {
return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate);
public static LoanTransaction disbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate disbursementDate, final String externalId) {
return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate, externalId);
}
public static LoanTransaction repayment(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) {
return new LoanTransaction(null, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate);
public static LoanTransaction repayment(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction repaymentAtDisbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) {
return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(), paymentDate);
public static LoanTransaction repaymentAtDisbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate);
return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate, null);
}
public static LoanTransaction applyInterest(final Loan loan, final Money amount, final LocalDate interestAppliedDate) {
return new LoanTransaction(loan, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate);
return new LoanTransaction(loan, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate, null);
}
public static LoanTransaction copyTransactionProperties(LoanTransaction loanTransaction) {
@@ -116,7 +120,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
public static LoanTransaction applyLoanCharge(final Loan loan, final Money amount, final LocalDate applyDate, final Money feeCharges,
final Money penaltyCharges) {
LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate);
LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate, null);
applyCharge.updateChargesComponents(feeCharges, penaltyCharges);
return applyCharge;
}
@@ -149,30 +153,32 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
public static LoanTransaction waiveLoanCharge(final Loan loan, final Money waived, final LocalDate waiveDate,
final Money feeChargesWaived, final Money penaltyChargesWaived) {
LoanTransaction waiver = new LoanTransaction(loan, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate);
LoanTransaction waiver = new LoanTransaction(loan, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate, null);
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived);
return waiver;
}
public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate) {
return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate);
public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate, final String externalId) {
return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId);
}
private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date, final String externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.amount = amount;
this.dateOf = date.toDateMidnight().toDate();
this.externalId = externalId;
}
private LoanTransaction(final Loan loan, final LoanTransactionType type, final PaymentDetail paymentDetail, final BigDecimal amount,
final LocalDate date) {
final LocalDate date, final String externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.paymentDetail = paymentDetail;
this.amount = amount;
this.dateOf = date.toDateMidnight().toDate();
this.externalId = externalId;
}
public void reverse() {
@@ -353,7 +359,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
paymentDetailData = paymentDetail.toData();
}
return new LoanTransactionData(this.getId(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount,
this.principalPortion, this.interestPortion, this.feeChargesPortion, this.penaltyChargesPortion);
this.principalPortion, this.interestPortion, this.feeChargesPortion, this.penaltyChargesPortion, this.externalId);
}
public Map<String, Object> toMapData(final CurrencyData currencyData) {
@@ -405,4 +411,8 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
this.loanChargesPaid = loanChargesPaid;
}
public String getExternalId() {
return externalId;
}
}
@@ -50,7 +50,7 @@ public final class LoanEventApiJsonValidator {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "note", "locale",
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
@@ -92,7 +92,7 @@ public final class LoanEventApiJsonValidator {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "note",
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
@@ -119,7 +119,7 @@ public final class LoanEventApiJsonValidator {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "note",
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
@@ -315,7 +315,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<CodeValueData> paymentOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
return new LoanTransactionData(null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions);
possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null);
}
@Override
@@ -339,7 +339,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount();
return new LoanTransactionData(null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, null, null,
null, null);
null, null, null);
}
@Override
@@ -348,7 +348,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
context.authenticatedUser();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null);
return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null, null);
}
@Override
@@ -359,7 +359,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<CodeValueData> paymentOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
return new LoanTransactionData(null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, null, null,
null, paymentOptions);
null, paymentOptions, null);
}
@Override
@@ -844,7 +844,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, "
+ " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
+ " cv.code_value as paymentTypeName " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
+ " cv.code_value as paymentTypeName, tr.external_id as externalId " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
+ " join m_currency rc on rc.`code` = l.currency_code "
+ " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id"
+ " left join m_code_value cv on pd.payment_type_cv_id = cv.id";
@@ -887,9 +887,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest");
final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees");
final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties");
final String externalId = rs.getString("externalId");
return new LoanTransactionData(id, transactionType, paymentDetailData, currencyData, date, totalAmount, principalPortion,
interestPortion, feeChargesPortion, penaltyChargesPortion);
interestPortion, feeChargesPortion, penaltyChargesPortion, externalId);
}
}
@@ -349,6 +349,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final String txnExternalId = command.stringValueOfParameterNamed("externalId");
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
@@ -364,7 +365,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount,
transactionDate, noteText);
transactionDate, noteText, txnExternalId);
return commandProcessingResultBuilder.withCommandId(command.commandId()) //
.withLoanId(loanId) //
@@ -373,7 +374,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final PaymentDetail paymentDetail,
final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText) {
final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText, final String txnExternalId) {
final Loan loan = retrieveLoanBy(loanId);
// TODO: Is it required to validate transaction date with meeting dates
@@ -393,7 +394,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate);
final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, txnExternalId);
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
@@ -448,7 +449,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
***/
PaymentDetail paymentDetail = null;
saveLoanRepayment(singleLoanRepaymentCommand.getLoanId(), paymentDetail, singleLoanRepaymentCommand.getTransactionAmount(),
bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote());
bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote(), null);
changes.put("bulkTransations", singleLoanRepaymentCommand);
}
return changes;
@@ -469,6 +470,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final String txnExternalId = command.stringValueOfParameterNamed("externalId");
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
@@ -481,7 +483,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createPaymentDetail(command, changes);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate, txnExternalId);
if (transactionToAdjust.isInterestWaiver()) {
newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate);
}