@Transactional
  @Override
  public EntityIdentifier waiveInterestOnLoan(final LoanTransactionCommand command) {

    context.authenticatedUser();

    final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
    validator.validate();

    final Loan loan = this.loanRepository.findOne(command.getLoanId());
    if (loan == null) {
      throw new LoanNotFoundException(command.getLoanId());
    }

    final LoanTransaction waiveTransaction =
        loan.waiveInterest(
            command.getTransactionAmount(),
            command.getTransactionDate(),
            defaultLoanLifecycleStateMachine());

    this.loanTransactionRepository.save(waiveTransaction);
    this.loanRepository.save(loan);

    final String noteText = command.getNote();
    if (StringUtils.isNotBlank(noteText)) {
      final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
      this.noteRepository.save(note);
    }

    return new EntityIdentifier(loan.getId());
  }
  @Transactional
  @Override
  public EntityIdentifier closeAsRescheduled(final LoanTransactionCommand command) {
    context.authenticatedUser();

    final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
    validator.validateNonMonetaryTransaction();

    final Loan loan = this.loanRepository.findOne(command.getLoanId());
    if (loan == null) {
      throw new LoanNotFoundException(command.getLoanId());
    }

    loan.closeAsMarkedForReschedule(
        command.getTransactionDate(), defaultLoanLifecycleStateMachine());

    this.loanRepository.save(loan);

    final String noteText = command.getNote();
    if (StringUtils.isNotBlank(noteText)) {
      final Note note = Note.loanNote(loan, noteText);
      this.noteRepository.save(note);
    }

    return new EntityIdentifier(loan.getId());
  }
  @Transactional
  @Override
  public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {

    AppUser currentUser = context.authenticatedUser();

    LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
    validator.validate();

    Loan loan = this.loanRepository.findOne(command.getLoanId());
    if (loan == null) {
      throw new LoanNotFoundException(command.getLoanId());
    }

    LocalDate transactionDate = command.getTransactionDate();
    if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) {
      throw new NoAuthorizationException(
          "error.msg.no.permission.to.make.repayment.on.loan.in.past");
    }

    Money repayment =
        Money.of(
            loan.repaymentScheduleDetail().getPrincipal().getCurrency(),
            command.getTransactionAmount());

    LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, transactionDate);
    loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
    this.loanTransactionRepository.save(loanRepayment);
    this.loanRepository.save(loan);

    String noteText = command.getNote();
    if (StringUtils.isNotBlank(noteText)) {
      Note note = Note.loanTransactionNote(loan, loanRepayment, noteText);
      this.noteRepository.save(note);
    }

    return new EntityIdentifier(loan.getId());
  }