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]

Reply via email to