adamsaghy commented on code in PR #5974:
URL: https://github.com/apache/fineract/pull/5974#discussion_r3434812432
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java:
##########
@@ -77,6 +110,156 @@ public CommandProcessingResult createLoanCharge(Long
loanId, JsonCommand command
.build();
}
+ @Transactional
+ @Override
+ public CommandProcessingResult adjustmentForLoanCharge(final Long loanId,
final Long wcLoanChargeId, final JsonCommand command) {
+
loanChargeDataValidator.validateChargeAdjustmentRequest(command.json());
+
+ final WorkingCapitalLoan loan =
workingCapitalLoanRepository.findById(loanId)
+ .orElseThrow(() -> new
WorkingCapitalLoanNotFoundException(loanId));
+ final WorkingCapitalLoanCharge wcCharge =
loanChargeRepository.findById(wcLoanChargeId)
+ .orElseThrow(() -> new
WorkingCapitalLoanChargeNotFoundException(wcLoanChargeId));
+
+ if (wcCharge.getLoan() == null ||
!loanId.equals(wcCharge.getLoan().getId())) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.charge.not.belongs.to.loan",
+ "Working capital loan charge " + wcLoanChargeId + " does
not belong to loan " + loanId);
+ }
+
+ final BigDecimal amount =
command.bigDecimalValueOfParameterNamed(WorkingCapitalLoanChargeConstants.amountParamName);
+ final LocalDate transactionDate = resolveTransactionDate(command);
+ final ExternalId externalId =
externalIdFactory.createFromCommand(command,
WorkingCapitalLoanChargeConstants.externalIdParamName);
+
+ chargeAdjustmentEntranceValidation(loan, wcCharge, amount);
+
+ final Map<String, Object> changes = new LinkedHashMap<>();
+ changes.put(WorkingCapitalLoanChargeConstants.amountParamName, amount);
+
changes.put(WorkingCapitalLoanChargeConstants.transactionDateParamName,
transactionDate);
+ changes.put(WorkingCapitalLoanChargeConstants.externalIdParamName,
externalId);
+
+ final PaymentDetail paymentDetail =
createAndPersistPaymentDetailFromCommand(command, changes);
+
+ final WorkingCapitalLoanTransaction adjustmentTx =
WorkingCapitalLoanTransaction.chargeAdjustment(loan, externalId, amount,
+ transactionDate, paymentDetail);
+
+ businessEventNotifierService
+ .notifyPreBusinessEvent(new
WorkingCapitalLoanChargeAdjustmentPreBusinessEvent(adjustmentTx, loan.getId()));
+
+ final WorkingCapitalLoanTransactionRelation relation =
WorkingCapitalLoanTransactionRelation.linkToCharge(adjustmentTx, wcCharge,
+ LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
+ adjustmentTx.getLoanTransactionRelations().add(relation);
+ transactionRepository.saveAndFlush(adjustmentTx);
+
+ final WorkingCapitalLoanTransactionAllocation allocation =
WorkingCapitalLoanTransactionAllocation.forChargeAdjustment(adjustmentTx,
+ amount, wcCharge.isPenaltyCharge());
+ allocationRepository.saveAndFlush(allocation);
+
+ applyChargeAmountPaid(wcCharge, amount);
+ applyBalanceAdjustment(loan, wcCharge, amount);
+
+ if (loan.getLoanProduct().getAccountingRule().isCashBased()) {
+ accountingProcessor.postJournalEntries(loan, adjustmentTx,
allocation, false);
+ }
+
+ final String noteText =
command.stringValueOfParameterNamed(WorkingCapitalLoanChargeConstants.noteParamName);
+ if (StringUtils.isNotBlank(noteText)) {
+ noteRepository.save(WorkingCapitalLoanNote.create(loan, noteText));
+ changes.put(WorkingCapitalLoanChargeConstants.noteParamName,
noteText);
+ }
+
+ businessEventNotifierService
+ .notifyPostBusinessEvent(new
WorkingCapitalLoanChargeAdjustmentPostBusinessEvent(adjustmentTx,
loan.getId()));
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(wcLoanChargeId) //
+ .withEntityExternalId(wcCharge.getExternalId()) //
+ .withSubEntityId(adjustmentTx.getId()) //
+ .withSubEntityExternalId(adjustmentTx.getExternalId()) //
+ .withOfficeId(loan.getOfficeId()) //
+ .withClientId(loan.getClientId()) //
+ .withLoanId(loanId) //
+ .with(changes) //
+ .build();
+ }
+
+ private LocalDate resolveTransactionDate(final JsonCommand command) {
+ final LocalDate requested =
command.localDateValueOfParameterNamed(WorkingCapitalLoanChargeConstants.transactionDateParamName);
+ return requested != null ? requested :
ThreadLocalContextUtil.getBusinessDate();
+ }
+
+ private void chargeAdjustmentEntranceValidation(final WorkingCapitalLoan
loan, final WorkingCapitalLoanCharge wcCharge,
+ final BigDecimal amount) {
+ if (loan.getLoanStatus() != LoanStatus.ACTIVE && loan.getLoanStatus()
!= LoanStatus.CLOSED_OBLIGATIONS_MET
+ && loan.getLoanStatus() != LoanStatus.OVERPAID) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.status",
+ "Adjustment is not supported for the status of " +
loan.getLoanStatus());
+ }
+
+ if (!wcCharge.isActive()) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.inactive.charge",
+ "Adjustment is not supported for inactive charges");
+ }
+
+ if (amount.compareTo(wcCharge.getAmount()) > 0) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.amount",
+ "Transaction amount cannot be higher than the charge
amount: " + wcCharge.getAmount());
+ }
+
+ final BigDecimal available =
calculateAvailableAmountForChargeAdjustment(wcCharge);
+ if (amount.compareTo(available) > 0) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.amount",
+ "Transaction amount cannot be higher than the available
charge amount for adjustment: " + available);
+ }
+
+ checkClientActive(loan);
+ }
+
+ private BigDecimal calculateAvailableAmountForChargeAdjustment(final
WorkingCapitalLoanCharge wcCharge) {
+ final BigDecimal previouslyAdjusted = relationRepository
+
.findAllByToChargeAndFromTransactionReversedAndFromTransactionTransactionType(wcCharge,
false,
+ LoanTransactionType.CHARGE_ADJUSTMENT)
+ .stream().map(rel ->
rel.getFromTransaction().getTransactionAmount()).reduce(BigDecimal.ZERO,
BigDecimal::add);
+ return wcCharge.getAmount().subtract(previouslyAdjusted);
+ }
+
+ private void checkClientActive(final WorkingCapitalLoan loan) {
+ if (loan.getClient() != null && loan.getClient().isNotActive()) {
+ throw new ClientNotActiveException(loan.getClient().getId());
+ }
+ }
+
+ private void applyChargeAmountPaid(final WorkingCapitalLoanCharge
wcCharge, final BigDecimal amount) {
+ final BigDecimal newPaid =
MathUtil.nullToZero(wcCharge.getAmountPaid()).add(amount);
+ wcCharge.setAmountPaid(newPaid);
+ if (newPaid.compareTo(MathUtil.nullToZero(wcCharge.getAmount())) >= 0)
{
+ wcCharge.setPaid(true);
+ }
+ loanChargeRepository.saveAndFlush(wcCharge);
Review Comment:
No need for this.
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java:
##########
@@ -77,6 +110,156 @@ public CommandProcessingResult createLoanCharge(Long
loanId, JsonCommand command
.build();
}
+ @Transactional
+ @Override
+ public CommandProcessingResult adjustmentForLoanCharge(final Long loanId,
final Long wcLoanChargeId, final JsonCommand command) {
+
loanChargeDataValidator.validateChargeAdjustmentRequest(command.json());
+
+ final WorkingCapitalLoan loan =
workingCapitalLoanRepository.findById(loanId)
+ .orElseThrow(() -> new
WorkingCapitalLoanNotFoundException(loanId));
+ final WorkingCapitalLoanCharge wcCharge =
loanChargeRepository.findById(wcLoanChargeId)
+ .orElseThrow(() -> new
WorkingCapitalLoanChargeNotFoundException(wcLoanChargeId));
+
+ if (wcCharge.getLoan() == null ||
!loanId.equals(wcCharge.getLoan().getId())) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.charge.not.belongs.to.loan",
+ "Working capital loan charge " + wcLoanChargeId + " does
not belong to loan " + loanId);
+ }
+
+ final BigDecimal amount =
command.bigDecimalValueOfParameterNamed(WorkingCapitalLoanChargeConstants.amountParamName);
+ final LocalDate transactionDate = resolveTransactionDate(command);
+ final ExternalId externalId =
externalIdFactory.createFromCommand(command,
WorkingCapitalLoanChargeConstants.externalIdParamName);
+
+ chargeAdjustmentEntranceValidation(loan, wcCharge, amount);
+
+ final Map<String, Object> changes = new LinkedHashMap<>();
+ changes.put(WorkingCapitalLoanChargeConstants.amountParamName, amount);
+
changes.put(WorkingCapitalLoanChargeConstants.transactionDateParamName,
transactionDate);
+ changes.put(WorkingCapitalLoanChargeConstants.externalIdParamName,
externalId);
+
+ final PaymentDetail paymentDetail =
createAndPersistPaymentDetailFromCommand(command, changes);
+
+ final WorkingCapitalLoanTransaction adjustmentTx =
WorkingCapitalLoanTransaction.chargeAdjustment(loan, externalId, amount,
+ transactionDate, paymentDetail);
+
+ businessEventNotifierService
+ .notifyPreBusinessEvent(new
WorkingCapitalLoanChargeAdjustmentPreBusinessEvent(adjustmentTx, loan.getId()));
+
+ final WorkingCapitalLoanTransactionRelation relation =
WorkingCapitalLoanTransactionRelation.linkToCharge(adjustmentTx, wcCharge,
+ LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
+ adjustmentTx.getLoanTransactionRelations().add(relation);
+ transactionRepository.saveAndFlush(adjustmentTx);
+
+ final WorkingCapitalLoanTransactionAllocation allocation =
WorkingCapitalLoanTransactionAllocation.forChargeAdjustment(adjustmentTx,
+ amount, wcCharge.isPenaltyCharge());
+ allocationRepository.saveAndFlush(allocation);
+
+ applyChargeAmountPaid(wcCharge, amount);
+ applyBalanceAdjustment(loan, wcCharge, amount);
+
+ if (loan.getLoanProduct().getAccountingRule().isCashBased()) {
+ accountingProcessor.postJournalEntries(loan, adjustmentTx,
allocation, false);
+ }
+
+ final String noteText =
command.stringValueOfParameterNamed(WorkingCapitalLoanChargeConstants.noteParamName);
+ if (StringUtils.isNotBlank(noteText)) {
+ noteRepository.save(WorkingCapitalLoanNote.create(loan, noteText));
+ changes.put(WorkingCapitalLoanChargeConstants.noteParamName,
noteText);
+ }
+
+ businessEventNotifierService
+ .notifyPostBusinessEvent(new
WorkingCapitalLoanChargeAdjustmentPostBusinessEvent(adjustmentTx,
loan.getId()));
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(wcLoanChargeId) //
+ .withEntityExternalId(wcCharge.getExternalId()) //
+ .withSubEntityId(adjustmentTx.getId()) //
+ .withSubEntityExternalId(adjustmentTx.getExternalId()) //
+ .withOfficeId(loan.getOfficeId()) //
+ .withClientId(loan.getClientId()) //
+ .withLoanId(loanId) //
+ .with(changes) //
+ .build();
+ }
+
+ private LocalDate resolveTransactionDate(final JsonCommand command) {
+ final LocalDate requested =
command.localDateValueOfParameterNamed(WorkingCapitalLoanChargeConstants.transactionDateParamName);
+ return requested != null ? requested :
ThreadLocalContextUtil.getBusinessDate();
+ }
+
+ private void chargeAdjustmentEntranceValidation(final WorkingCapitalLoan
loan, final WorkingCapitalLoanCharge wcCharge,
+ final BigDecimal amount) {
+ if (loan.getLoanStatus() != LoanStatus.ACTIVE && loan.getLoanStatus()
!= LoanStatus.CLOSED_OBLIGATIONS_MET
+ && loan.getLoanStatus() != LoanStatus.OVERPAID) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.status",
+ "Adjustment is not supported for the status of " +
loan.getLoanStatus());
+ }
+
+ if (!wcCharge.isActive()) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.inactive.charge",
+ "Adjustment is not supported for inactive charges");
+ }
+
+ if (amount.compareTo(wcCharge.getAmount()) > 0) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.amount",
+ "Transaction amount cannot be higher than the charge
amount: " + wcCharge.getAmount());
+ }
+
+ final BigDecimal available =
calculateAvailableAmountForChargeAdjustment(wcCharge);
+ if (amount.compareTo(available) > 0) {
+ throw new
WorkingCapitalLoanChargeAdjustmentException("wc.loan.charge.adjustment.invalid.amount",
+ "Transaction amount cannot be higher than the available
charge amount for adjustment: " + available);
+ }
+
+ checkClientActive(loan);
+ }
+
+ private BigDecimal calculateAvailableAmountForChargeAdjustment(final
WorkingCapitalLoanCharge wcCharge) {
+ final BigDecimal previouslyAdjusted = relationRepository
+
.findAllByToChargeAndFromTransactionReversedAndFromTransactionTransactionType(wcCharge,
false,
+ LoanTransactionType.CHARGE_ADJUSTMENT)
+ .stream().map(rel ->
rel.getFromTransaction().getTransactionAmount()).reduce(BigDecimal.ZERO,
BigDecimal::add);
+ return wcCharge.getAmount().subtract(previouslyAdjusted);
+ }
+
+ private void checkClientActive(final WorkingCapitalLoan loan) {
+ if (loan.getClient() != null && loan.getClient().isNotActive()) {
+ throw new ClientNotActiveException(loan.getClient().getId());
+ }
+ }
+
+ private void applyChargeAmountPaid(final WorkingCapitalLoanCharge
wcCharge, final BigDecimal amount) {
+ final BigDecimal newPaid =
MathUtil.nullToZero(wcCharge.getAmountPaid()).add(amount);
+ wcCharge.setAmountPaid(newPaid);
+ if (newPaid.compareTo(MathUtil.nullToZero(wcCharge.getAmount())) >= 0)
{
+ wcCharge.setPaid(true);
+ }
+ loanChargeRepository.saveAndFlush(wcCharge);
+ }
+
+ private void applyBalanceAdjustment(final WorkingCapitalLoan loan, final
WorkingCapitalLoanCharge wcCharge, final BigDecimal amount) {
+ final WorkingCapitalLoanBalance balance =
balanceRepository.findByWcLoan_Id(loan.getId())
+ .orElseGet(() -> WorkingCapitalLoanBalance.createFor(loan));
+ if (wcCharge.isPenaltyCharge()) {
+
balance.setPenaltyPaid(MathUtil.nullToZero(balance.getPenaltyPaid()).add(amount));
+ } else {
+
balance.setFeePaid(MathUtil.nullToZero(balance.getFeePaid()).add(amount));
+ }
+ balanceRepository.saveAndFlush(balance);
Review Comment:
not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]