Search in sources :

Example 1 with InvoiceWorkflowDataHolder

use of org.folio.models.InvoiceWorkflowDataHolder in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidatorTest method checkEnoughMoneyInBudgetShouldThrowFundCannotBePaidIfTransactionsAmountDifferenceGreaterThanBudgetRemainingAmount.

@Test
void checkEnoughMoneyInBudgetShouldThrowFundCannotBePaidIfTransactionsAmountDifferenceGreaterThanBudgetRemainingAmount() {
    String fiscalYearId = UUID.randomUUID().toString();
    String fundId = UUID.randomUUID().toString();
    String budgetId = UUID.randomUUID().toString();
    String ledgerId = UUID.randomUUID().toString();
    Transaction existingTransaction = new Transaction().withTransactionType(Transaction.TransactionType.PENDING_PAYMENT).withAmount(50d).withFiscalYearId(fiscalYearId).withFromFundId(fundId).withCurrency("USD");
    Transaction newTransaction = new Transaction().withTransactionType(Transaction.TransactionType.PENDING_PAYMENT).withAmount(60d).withFiscalYearId(fiscalYearId).withFromFundId(fundId).withCurrency("USD");
    Fund fund = new Fund().withId(fundId).withName("TestFund").withLedgerId(ledgerId).withCode("FC").withFundStatus(Fund.FundStatus.ACTIVE);
    Budget budget = new Budget().withId(budgetId).withFiscalYearId(fiscalYearId).withFundId(fundId).withAllocated(59d).withTotalFunding(59d).withAvailable(9d).withUnavailable(50d).withAwaitingPayment(50D).withAllowableExpenditure(100d);
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    InvoiceWorkflowDataHolder holder = new InvoiceWorkflowDataHolder().withFund(fund).withExistingTransaction(existingTransaction).withNewTransaction(newTransaction).withBudget(budget).withRestrictExpenditures(true).withFiscalYear(new FiscalYear().withId(fiscalYearId).withCurrency("USD"));
    holders.add(holder);
    HttpException httpException = assertThrows(HttpException.class, () -> fundAvailabilityValidator.validate(holders));
    assertEquals(422, httpException.getCode());
    Error error = httpException.getErrors().getErrors().get(0);
    assertEquals(FUND_CANNOT_BE_PAID.getCode(), error.getCode());
    assertEquals(Collections.singletonList("FC").toString(), error.getParameters().get(0).getValue());
}
Also used : FiscalYear(org.folio.rest.acq.model.finance.FiscalYear) Transaction(org.folio.rest.acq.model.finance.Transaction) Fund(org.folio.rest.acq.model.finance.Fund) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) ArrayList(java.util.ArrayList) Error(org.folio.rest.jaxrs.model.Error) Budget(org.folio.rest.acq.model.finance.Budget) HttpException(org.folio.invoices.rest.exceptions.HttpException) Test(org.junit.jupiter.api.Test)

Example 2 with InvoiceWorkflowDataHolder

use of org.folio.models.InvoiceWorkflowDataHolder in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidatorTest method checkEnoughMoneyInBudgetShouldPassIfTransactionsAmountDifferenceGreaterThanBudgetRemainingAmountAndBudgetAllowableExpenditureIsNull.

@Test
void checkEnoughMoneyInBudgetShouldPassIfTransactionsAmountDifferenceGreaterThanBudgetRemainingAmountAndBudgetAllowableExpenditureIsNull() {
    String fiscalYearId = UUID.randomUUID().toString();
    String fundId = UUID.randomUUID().toString();
    String budgetId = UUID.randomUUID().toString();
    String ledgerId = UUID.randomUUID().toString();
    Transaction existingTransaction = new Transaction().withTransactionType(Transaction.TransactionType.PENDING_PAYMENT).withAmount(50d).withFiscalYearId(fiscalYearId).withFromFundId(fundId).withCurrency("USD");
    Transaction newTransaction = new Transaction().withTransactionType(Transaction.TransactionType.PENDING_PAYMENT).withAmount(60d).withFiscalYearId(fiscalYearId).withFromFundId(fundId).withCurrency("USD");
    Fund fund = new Fund().withId(fundId).withName("TestFund").withLedgerId(ledgerId).withCode("FC").withFundStatus(Fund.FundStatus.ACTIVE);
    Budget budget = new Budget().withId(budgetId).withFiscalYearId(fiscalYearId).withFundId(fundId).withAllocated(59d).withTotalFunding(59d).withAvailable(0d).withUnavailable(50d).withAwaitingPayment(50D).withAllowableExpenditure(null);
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    InvoiceWorkflowDataHolder holder = new InvoiceWorkflowDataHolder().withFund(fund).withExistingTransaction(existingTransaction).withNewTransaction(newTransaction).withBudget(budget).withRestrictExpenditures(true).withFiscalYear(new FiscalYear().withId(fiscalYearId).withCurrency("USD"));
    holders.add(holder);
    assertDoesNotThrow(() -> fundAvailabilityValidator.validate(holders));
}
Also used : FiscalYear(org.folio.rest.acq.model.finance.FiscalYear) Transaction(org.folio.rest.acq.model.finance.Transaction) Fund(org.folio.rest.acq.model.finance.Fund) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) ArrayList(java.util.ArrayList) Budget(org.folio.rest.acq.model.finance.Budget) Test(org.junit.jupiter.api.Test)

Example 3 with InvoiceWorkflowDataHolder

use of org.folio.models.InvoiceWorkflowDataHolder in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidatorTest method shouldPassValidationWhenBudgetRestrictedAndFinalExpendedValueGreaterThenMaxBudgetExpended.

@Test
void shouldPassValidationWhenBudgetRestrictedAndFinalExpendedValueGreaterThenMaxBudgetExpended() {
    String fiscalYearId = UUID.randomUUID().toString();
    String fundId = UUID.randomUUID().toString();
    String budgetId = UUID.randomUUID().toString();
    String ledgerId = UUID.randomUUID().toString();
    FiscalYear fiscalYear = new FiscalYear().withCurrency("USD").withId(fiscalYearId);
    Fund fund = new Fund().withId(fundId).withName("TestFund").withLedgerId(ledgerId).withCode("FC").withFundStatus(Fund.FundStatus.ACTIVE);
    Budget budget = new Budget().withId(budgetId).withFiscalYearId(fiscalYearId).withFundId(fundId).withAllocated(260d).withTotalFunding(260d).withAvailable(0d).withUnavailable(290d).withEncumbered(250d).withAwaitingPayment(30d).withExpenditures(10d).withAllowableExpenditure(100d).withAllowableExpenditure(110d);
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    Transaction encumbrance = new Transaction().withId(UUID.randomUUID().toString()).withAmount(250d).withCurrency("USD");
    Transaction linePendingPayment = new Transaction().withAmount(245d).withAwaitingPayment(new AwaitingPayment().withEncumbranceId(encumbrance.getId()).withReleaseEncumbrance(false)).withCurrency("USD");
    InvoiceWorkflowDataHolder holder = new InvoiceWorkflowDataHolder().withFund(fund).withBudget(budget).withRestrictExpenditures(true).withFiscalYear(fiscalYear).withNewTransaction(linePendingPayment).withEncumbrance(encumbrance);
    holders.add(holder);
    HttpException httpException = assertThrows(HttpException.class, () -> fundAvailabilityValidator.validate(holders));
    assertEquals(422, httpException.getCode());
    Error error = httpException.getErrors().getErrors().get(0);
    assertEquals(FUND_CANNOT_BE_PAID.getCode(), error.getCode());
    assertEquals(Collections.singletonList("FC").toString(), error.getParameters().get(0).getValue());
}
Also used : FiscalYear(org.folio.rest.acq.model.finance.FiscalYear) Fund(org.folio.rest.acq.model.finance.Fund) Transaction(org.folio.rest.acq.model.finance.Transaction) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) ArrayList(java.util.ArrayList) Error(org.folio.rest.jaxrs.model.Error) Budget(org.folio.rest.acq.model.finance.Budget) HttpException(org.folio.invoices.rest.exceptions.HttpException) AwaitingPayment(org.folio.rest.acq.model.finance.AwaitingPayment) Test(org.junit.jupiter.api.Test)

Example 4 with InvoiceWorkflowDataHolder

use of org.folio.models.InvoiceWorkflowDataHolder in project mod-invoice by folio-org.

the class BudgetExpenseClassTest method shouldThrowExceptionWithBudgetExpenseClassNotFoundCodeWhenCheckExpenseClassesWithoutExpenseClasses.

@Test
void shouldThrowExceptionWithBudgetExpenseClassNotFoundCodeWhenCheckExpenseClassesWithoutExpenseClasses() {
    String notAssignedExpenseClassId = UUID.randomUUID().toString();
    String activeExpenseClassId = UUID.randomUUID().toString();
    FundDistribution fundDistributionWithActiveExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString()).withExpenseClassId(activeExpenseClassId);
    FundDistribution fundDistributionWithNotAssignedExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString()).withExpenseClassId(notAssignedExpenseClassId);
    FundDistribution fundDistributionWithoutExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString());
    InvoiceLine invoiceLine = new InvoiceLine().withFundDistributions(Arrays.asList(fundDistributionWithActiveExpenseClass, fundDistributionWithoutExpenseClass));
    List<InvoiceLine> invoiceLines = Collections.singletonList(invoiceLine);
    Adjustment adjustment = new Adjustment().withFundDistributions(Collections.singletonList(fundDistributionWithNotAssignedExpenseClass));
    Invoice invoice = new Invoice().withAdjustments(Collections.singletonList(adjustment));
    BudgetExpenseClass active = new BudgetExpenseClass().withBudgetId(UUID.randomUUID().toString()).withExpenseClassId(activeExpenseClassId).withStatus(BudgetExpenseClass.Status.ACTIVE).withId(UUID.randomUUID().toString());
    Fund noExpenseClassFund = new Fund().withCode("no expense class fund");
    ExpenseClass notAssignedExpenseClass = new ExpenseClass().withName("not assigned class");
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    InvoiceWorkflowDataHolder holder1 = new InvoiceWorkflowDataHolder().withInvoice(invoice).withInvoiceLine(invoiceLine).withFundDistribution(fundDistributionWithActiveExpenseClass).withBudget(new Budget().withFundId(UUID.randomUUID().toString())).withExpenseClass(new ExpenseClass().withId(activeExpenseClassId));
    InvoiceWorkflowDataHolder holder2 = new InvoiceWorkflowDataHolder().withInvoice(invoice).withInvoiceLine(invoiceLine).withBudget(new Budget().withFundId(UUID.randomUUID().toString())).withFundDistribution(fundDistributionWithoutExpenseClass);
    InvoiceWorkflowDataHolder holder3 = new InvoiceWorkflowDataHolder().withInvoice(invoice).withAdjustment(adjustment).withFundDistribution(fundDistributionWithNotAssignedExpenseClass).withExpenseClass(notAssignedExpenseClass).withBudget(new Budget().withFundId(UUID.randomUUID().toString())).withFund(noExpenseClassFund);
    holders.add(holder1);
    holders.add(holder2);
    holders.add(holder3);
    when(restClient.get(any(RequestEntry.class), any(), any())).thenAnswer(invocation -> {
        RequestEntry requestEntry = invocation.getArgument(0);
        List<BudgetExpenseClass> budgetExpenseClasses = requestEntry.buildEndpoint().contains(notAssignedExpenseClassId) ? Collections.emptyList() : Collections.singletonList(active);
        return CompletableFuture.completedFuture(new BudgetExpenseClassCollection().withBudgetExpenseClasses(budgetExpenseClasses).withTotalRecords(1));
    });
    when(requestContext.getContext()).thenReturn(Vertx.vertx().getOrCreateContext());
    CompletableFuture<List<InvoiceWorkflowDataHolder>> future = budgetExpenseClassService.checkExpenseClasses(holders, requestContext);
    ExecutionException executionException = assertThrows(ExecutionException.class, future::get);
    assertThat(executionException.getCause(), instanceOf(HttpException.class));
    HttpException exception = (HttpException) executionException.getCause();
    assertEquals(400, exception.getCode());
    Errors errors = exception.getErrors();
    Error error = errors.getErrors().get(0);
    assertEquals(BUDGET_EXPENSE_CLASS_NOT_FOUND.getCode(), error.getCode());
    String expenseClassNameFromError = error.getParameters().stream().filter(parameter -> parameter.getKey().equals(EXPENSE_CLASS_NAME)).findFirst().get().getValue();
    assertEquals(notAssignedExpenseClass.getName(), expenseClassNameFromError);
    String fundCodeFromError = error.getParameters().stream().filter(parameter -> parameter.getKey().equals(FUND_CODE)).findFirst().get().getValue();
    assertEquals(noExpenseClassFund.getCode(), fundCodeFromError);
}
Also used : ExpenseClass(org.folio.rest.acq.model.finance.ExpenseClass) BudgetExpenseClass(org.folio.rest.acq.model.finance.BudgetExpenseClass) Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) RestClient(org.folio.rest.core.RestClient) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) Mock(org.mockito.Mock) CompletableFuture(java.util.concurrent.CompletableFuture) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) ArrayList(java.util.ArrayList) HttpException(org.folio.invoices.rest.exceptions.HttpException) MockitoAnnotations(org.mockito.MockitoAnnotations) ExpenseClass(org.folio.rest.acq.model.finance.ExpenseClass) FUND_CODE(org.folio.services.finance.budget.BudgetExpenseClassService.FUND_CODE) BudgetExpenseClass(org.folio.rest.acq.model.finance.BudgetExpenseClass) RequestContext(org.folio.rest.core.models.RequestContext) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Errors(org.folio.rest.jaxrs.model.Errors) Budget(org.folio.rest.acq.model.finance.Budget) InvoiceLine(org.folio.rest.jaxrs.model.InvoiceLine) INACTIVE_EXPENSE_CLASS(org.folio.invoices.utils.ErrorCodes.INACTIVE_EXPENSE_CLASS) BudgetExpenseClassService(org.folio.services.finance.budget.BudgetExpenseClassService) Vertx(io.vertx.core.Vertx) RequestEntry(org.folio.rest.core.models.RequestEntry) Mockito.when(org.mockito.Mockito.when) UUID(java.util.UUID) Fund(org.folio.rest.acq.model.finance.Fund) FundDistribution(org.folio.rest.jaxrs.model.FundDistribution) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) ExecutionException(java.util.concurrent.ExecutionException) Test(org.junit.jupiter.api.Test) Error(org.folio.rest.jaxrs.model.Error) List(java.util.List) BudgetExpenseClassCollection(org.folio.rest.acq.model.finance.BudgetExpenseClassCollection) EXPENSE_CLASS_NAME(org.folio.services.finance.budget.BudgetExpenseClassService.EXPENSE_CLASS_NAME) Adjustment(org.folio.rest.jaxrs.model.Adjustment) BUDGET_EXPENSE_CLASS_NOT_FOUND(org.folio.invoices.utils.ErrorCodes.BUDGET_EXPENSE_CLASS_NOT_FOUND) Collections(java.util.Collections) Invoice(org.folio.rest.jaxrs.model.Invoice) Invoice(org.folio.rest.jaxrs.model.Invoice) Adjustment(org.folio.rest.jaxrs.model.Adjustment) BudgetExpenseClass(org.folio.rest.acq.model.finance.BudgetExpenseClass) Fund(org.folio.rest.acq.model.finance.Fund) InvoiceLine(org.folio.rest.jaxrs.model.InvoiceLine) ArrayList(java.util.ArrayList) Error(org.folio.rest.jaxrs.model.Error) RequestEntry(org.folio.rest.core.models.RequestEntry) FundDistribution(org.folio.rest.jaxrs.model.FundDistribution) Errors(org.folio.rest.jaxrs.model.Errors) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) Budget(org.folio.rest.acq.model.finance.Budget) ArrayList(java.util.ArrayList) List(java.util.List) HttpException(org.folio.invoices.rest.exceptions.HttpException) ExecutionException(java.util.concurrent.ExecutionException) BudgetExpenseClassCollection(org.folio.rest.acq.model.finance.BudgetExpenseClassCollection) Test(org.junit.jupiter.api.Test)

Example 5 with InvoiceWorkflowDataHolder

use of org.folio.models.InvoiceWorkflowDataHolder in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidator method calculateNewExpendedAmount.

private MonetaryAmount calculateNewExpendedAmount(List<InvoiceWorkflowDataHolder> dataHolders) {
    CurrencyUnit currency = Monetary.getCurrency(dataHolders.get(0).getFyCurrency());
    return dataHolders.stream().map(holder -> {
        MonetaryAmount newTransactionAmount = Money.of(holder.getNewTransaction().getAmount(), holder.getFyCurrency());
        MonetaryAmount existingTransactionAmount = Optional.ofNullable(holder.getExistingTransaction()).map(transaction -> Money.of(transaction.getAmount(), transaction.getCurrency())).orElseGet(() -> Money.zero(Monetary.getCurrency(holder.getFyCurrency())));
        MonetaryAmount encumbranceAmount = Optional.ofNullable(holder.getEncumbrance()).map(transaction -> Money.of(transaction.getAmount(), transaction.getCurrency())).orElseGet(() -> Money.zero(Monetary.getCurrency(holder.getFyCurrency())));
        MonetaryAmount transactionAmountDif = newTransactionAmount.subtract(existingTransactionAmount);
        MonetaryAmount newExpendedAmount = transactionAmountDif;
        if (transactionAmountDif.isPositive()) {
            newExpendedAmount = MonetaryFunctions.max().apply(transactionAmountDif.subtract(encumbranceAmount), Money.zero(currency));
            MonetaryAmount encumbranceReminder = MonetaryFunctions.max().apply(encumbranceAmount.subtract(newExpendedAmount).add(existingTransactionAmount), Money.zero(currency));
            Optional.ofNullable(holder.getEncumbrance()).ifPresent(transaction -> transaction.setAmount(encumbranceReminder.getNumber().doubleValue()));
        }
        return newExpendedAmount;
    }).reduce(MonetaryFunctions::sum).orElseGet(() -> Money.zero(currency));
}
Also used : Monetary(javax.money.Monetary) FUND_CANNOT_BE_PAID(org.folio.invoices.utils.ErrorCodes.FUND_CANNOT_BE_PAID) CurrencyUnit(javax.money.CurrencyUnit) Money(org.javamoney.moneta.Money) Collectors.groupingBy(java.util.stream.Collectors.groupingBy) MonetaryFunctions(org.javamoney.moneta.function.MonetaryFunctions) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) Fund(org.folio.rest.acq.model.finance.Fund) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BigDecimal(java.math.BigDecimal) HttpException(org.folio.invoices.rest.exceptions.HttpException) Collectors.toList(java.util.stream.Collectors.toList) List(java.util.List) FUNDS(org.folio.invoices.utils.ResourcePathResolver.FUNDS) Map(java.util.Map) Optional(java.util.Optional) Parameter(org.folio.rest.jaxrs.model.Parameter) Collections(java.util.Collections) MonetaryAmount(javax.money.MonetaryAmount) Budget(org.folio.rest.acq.model.finance.Budget) CurrencyUnit(javax.money.CurrencyUnit) MonetaryAmount(javax.money.MonetaryAmount)

Aggregations

InvoiceWorkflowDataHolder (org.folio.models.InvoiceWorkflowDataHolder)17 ArrayList (java.util.ArrayList)12 HttpException (org.folio.invoices.rest.exceptions.HttpException)11 Budget (org.folio.rest.acq.model.finance.Budget)11 Fund (org.folio.rest.acq.model.finance.Fund)11 Transaction (org.folio.rest.acq.model.finance.Transaction)11 List (java.util.List)10 Test (org.junit.jupiter.api.Test)9 MonetaryAmount (javax.money.MonetaryAmount)7 FiscalYear (org.folio.rest.acq.model.finance.FiscalYear)7 Optional (java.util.Optional)6 Collectors.toList (java.util.stream.Collectors.toList)6 RequestContext (org.folio.rest.core.models.RequestContext)6 InvoiceLine (org.folio.rest.jaxrs.model.InvoiceLine)6 Collections (java.util.Collections)5 CompletableFuture (java.util.concurrent.CompletableFuture)5 Error (org.folio.rest.jaxrs.model.Error)5 FundDistribution (org.folio.rest.jaxrs.model.FundDistribution)5 Invoice (org.folio.rest.jaxrs.model.Invoice)5 Map (java.util.Map)4