Search in sources :

Example 21 with Budget

use of org.folio.rest.acq.model.finance.Budget in project mod-invoice by folio-org.

the class InvoicesApiTest method testTransitionFromOpenToApprovedWithRestrictExpenditures.

@Test
void testTransitionFromOpenToApprovedWithRestrictExpenditures() {
    Invoice reqData = getMockAsJson(OPEN_INVOICE_SAMPLE_PATH).mapTo(Invoice.class);
    String id = reqData.getId();
    InvoiceLine invoiceLine = getMinimalContentInvoiceLine(id);
    invoiceLine.setSubTotal(50d);
    invoiceLine.setId(UUID.randomUUID().toString());
    String fundId = UUID.randomUUID().toString();
    String ledgerId = UUID.randomUUID().toString();
    Budget budget = new Budget().withId(UUID.randomUUID().toString()).withFundId(fundId).withFiscalYearId(FISCAL_YEAR_ID).withAllocated(50d).withAvailable(50d).withBudgetStatus(BudgetStatus.ACTIVE).withUnavailable(0d);
    Fund fund = new Fund().withId(fundId).withExternalAccountNo("test").withLedgerId(ledgerId).withCode("FC").withFundStatus(Fund.FundStatus.ACTIVE);
    Ledger ledger = new Ledger().withId(ledgerId).withRestrictExpenditures(false);
    FundDistribution amountDistribution = new FundDistribution().withFundId(fundId).withDistributionType(AMOUNT).withValue(50d);
    invoiceLine.getFundDistributions().add(amountDistribution);
    addMockEntry(INVOICE_LINES, JsonObject.mapFrom(invoiceLine));
    addMockEntry(BUDGETS, JsonObject.mapFrom(budget));
    addMockEntry(FUNDS, JsonObject.mapFrom(fund));
    addMockEntry(LEDGERS, JsonObject.mapFrom(ledger));
    reqData.setStatus(Invoice.Status.APPROVED);
    String jsonBody = JsonObject.mapFrom(reqData).encode();
    Headers headers = prepareHeaders(X_OKAPI_URL, X_OKAPI_TENANT, X_OKAPI_TOKEN, X_OKAPI_USER_ID);
    verifyPut(String.format(INVOICE_ID_PATH, id), jsonBody, headers, "", 204);
}
Also used : FundDistribution(org.folio.rest.jaxrs.model.FundDistribution) Invoice(org.folio.rest.jaxrs.model.Invoice) Fund(org.folio.rest.acq.model.finance.Fund) InvoiceLine(org.folio.rest.jaxrs.model.InvoiceLine) Headers(io.restassured.http.Headers) Ledger(org.folio.rest.acq.model.finance.Ledger) Budget(org.folio.rest.acq.model.finance.Budget) Matchers.containsString(org.hamcrest.Matchers.containsString) Test(org.junit.jupiter.api.Test)

Example 22 with Budget

use of org.folio.rest.acq.model.finance.Budget in project mod-invoice by folio-org.

the class BudgetExpenseClassTest method shouldThrowExceptionWithInactiveExpenseClassCodeWhenCheckExpenseClassesWithInactiveExpenseClass.

@Test
void shouldThrowExceptionWithInactiveExpenseClassCodeWhenCheckExpenseClassesWithInactiveExpenseClass() {
    String inactiveExpenseClassId = UUID.randomUUID().toString();
    String activeExpenseClassId = UUID.randomUUID().toString();
    FundDistribution fundDistributionWithInactiveExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString()).withExpenseClassId(inactiveExpenseClassId);
    FundDistribution fundDistributionWithActiveExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString()).withExpenseClassId(activeExpenseClassId);
    FundDistribution fundDistributionWithoutExpenseClass = new FundDistribution().withFundId(UUID.randomUUID().toString());
    InvoiceLine invoiceLine = new InvoiceLine().withFundDistributions(Arrays.asList(fundDistributionWithInactiveExpenseClass, fundDistributionWithoutExpenseClass));
    List<InvoiceLine> invoiceLines = Collections.singletonList(invoiceLine);
    Adjustment adjustment = new Adjustment().withFundDistributions(Collections.singletonList(fundDistributionWithActiveExpenseClass));
    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());
    BudgetExpenseClass inactive = new BudgetExpenseClass().withBudgetId(UUID.randomUUID().toString()).withExpenseClassId(inactiveExpenseClassId).withStatus(BudgetExpenseClass.Status.INACTIVE).withId(UUID.randomUUID().toString());
    Fund inactiveExpenseClassFund = new Fund().withCode("inactive fund");
    ExpenseClass inactiveExpenseClass = new ExpenseClass().withName("inactive class");
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    InvoiceWorkflowDataHolder holder1 = new InvoiceWorkflowDataHolder().withInvoice(invoice).withInvoiceLine(invoiceLine).withFundDistribution(fundDistributionWithInactiveExpenseClass).withExpenseClass(inactiveExpenseClass).withBudget(new Budget().withFundId(UUID.randomUUID().toString())).withFund(inactiveExpenseClassFund);
    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(fundDistributionWithActiveExpenseClass).withBudget(new Budget().withFundId(UUID.randomUUID().toString())).withExpenseClass(new ExpenseClass().withId(activeExpenseClassId));
    holders.add(holder1);
    holders.add(holder2);
    holders.add(holder3);
    when(restClient.get(any(RequestEntry.class), any(), any())).thenAnswer(invocation -> {
        RequestEntry requestEntry = invocation.getArgument(0);
        BudgetExpenseClass bec = requestEntry.buildEndpoint().contains(activeExpenseClassId) ? active : inactive;
        return CompletableFuture.completedFuture(new BudgetExpenseClassCollection().withBudgetExpenseClasses(Collections.singletonList(bec)).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(INACTIVE_EXPENSE_CLASS.getCode(), error.getCode());
    String expenseClassNameFromError = error.getParameters().stream().filter(parameter -> parameter.getKey().equals(EXPENSE_CLASS_NAME)).findFirst().get().getValue();
    assertEquals(inactiveExpenseClass.getName(), expenseClassNameFromError);
    String fundCodeFromError = error.getParameters().stream().filter(parameter -> parameter.getKey().equals(FUND_CODE)).findFirst().get().getValue();
    assertEquals(inactiveExpenseClassFund.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 23 with Budget

use of org.folio.rest.acq.model.finance.Budget in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidator method validate.

@Override
public void validate(List<InvoiceWorkflowDataHolder> dataHolders) {
    Map<Budget, List<InvoiceWorkflowDataHolder>> budgetHoldersMap = dataHolders.stream().filter(InvoiceWorkflowDataHolder::isRestrictExpenditures).collect(groupingBy(InvoiceWorkflowDataHolder::getBudget));
    Map<String, String> fundHoldersMap = dataHolders.stream().filter(InvoiceWorkflowDataHolder::isRestrictExpenditures).map(InvoiceWorkflowDataHolder::getFund).filter(Objects::nonNull).collect(Collectors.toMap(Fund::getId, Fund::getCode, (fundEntityKey, fundEntityDupKey) -> fundEntityKey));
    List<String> failedBudgetIds = budgetHoldersMap.entrySet().stream().filter(entry -> Objects.nonNull(entry.getKey().getAllowableExpenditure())).filter(entry -> {
        MonetaryAmount newExpendedAmount = calculateNewExpendedAmount(entry.getValue());
        MonetaryAmount totalExpendedAmount = calculateTotalExpendedAmount(entry.getValue());
        return isRemainingAmountExceed(entry.getKey(), newExpendedAmount, totalExpendedAmount);
    }).map(Map.Entry::getKey).map(Budget::getFundId).collect(toList());
    if (!failedBudgetIds.isEmpty()) {
        Parameter parameter = new Parameter().withKey(FUNDS).withValue(failedBudgetIds.stream().map(fundHoldersMap::get).collect(toList()).toString());
        throw new HttpException(422, FUND_CANNOT_BE_PAID.toError().withParameters(Collections.singletonList(parameter)));
    }
}
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) MonetaryAmount(javax.money.MonetaryAmount) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) Parameter(org.folio.rest.jaxrs.model.Parameter) Budget(org.folio.rest.acq.model.finance.Budget) Collectors.toList(java.util.stream.Collectors.toList) List(java.util.List) HttpException(org.folio.invoices.rest.exceptions.HttpException)

Example 24 with Budget

use of org.folio.rest.acq.model.finance.Budget in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidatorTest method checkEnoughMoneyInBudgetShouldPassIfTransactionsAmountDifferenceLessThanBudgetRemainingAmount.

@Test
void checkEnoughMoneyInBudgetShouldPassIfTransactionsAmountDifferenceLessThanBudgetRemainingAmount() {
    String fiscalYearId = UUID.randomUUID().toString();
    String fundId = UUID.randomUUID().toString();
    String ledgerId = UUID.randomUUID().toString();
    String budgetId = 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");
    Budget budget = new Budget().withId(budgetId).withFiscalYearId(fiscalYearId).withFundId(fundId).withAllocated(59d).withAvailable(9d).withTotalFunding(59d).withUnavailable(50d).withAwaitingPayment(50D).withAllowableExpenditure(150d);
    Fund fund = new Fund().withId(fundId).withLedgerId(ledgerId).withCode("FC").withFundStatus(Fund.FundStatus.ACTIVE);
    Ledger ledger = new Ledger().withId(ledgerId).withRestrictExpenditures(true);
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    InvoiceWorkflowDataHolder holder = new InvoiceWorkflowDataHolder().withExistingTransaction(existingTransaction).withNewTransaction(newTransaction).withBudget(budget).withFund(fund).withRestrictExpenditures(ledger.getRestrictExpenditures()).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) Ledger(org.folio.rest.acq.model.finance.Ledger) InvoiceWorkflowDataHolder(org.folio.models.InvoiceWorkflowDataHolder) ArrayList(java.util.ArrayList) Budget(org.folio.rest.acq.model.finance.Budget) Test(org.junit.jupiter.api.Test)

Example 25 with Budget

use of org.folio.rest.acq.model.finance.Budget in project mod-invoice by folio-org.

the class FundAvailabilityHolderValidatorTest method shouldPassValidationWhenBudgetRestrictedAndAmountGreaterThanAvailableAndRequiredAmountEncumbered.

@Test
void shouldPassValidationWhenBudgetRestrictedAndAmountGreaterThanAvailableAndRequiredAmountEncumbered() {
    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(20d).withUnavailable(240d).withEncumbered(200d).withAwaitingPayment(40d).withAllowableExpenditure(100d);
    List<InvoiceWorkflowDataHolder> holders = new ArrayList<>();
    Transaction adjustmentPendingPayment = new Transaction().withAmount(20d).withCurrency("USD");
    Transaction encumbrance = new Transaction().withId(UUID.randomUUID().toString()).withAmount(200d).withCurrency("USD");
    Transaction linePendingPayment = new Transaction().withAmount(200d).withAwaitingPayment(new AwaitingPayment().withEncumbranceId(encumbrance.getId()).withReleaseEncumbrance(false)).withCurrency("USD");
    InvoiceWorkflowDataHolder holder1 = new InvoiceWorkflowDataHolder().withFund(fund).withBudget(budget).withRestrictExpenditures(true).withFiscalYear(fiscalYear).withNewTransaction(linePendingPayment).withEncumbrance(encumbrance);
    InvoiceWorkflowDataHolder holder2 = new InvoiceWorkflowDataHolder().withFund(fund).withBudget(budget).withRestrictExpenditures(true).withFiscalYear(fiscalYear).withNewTransaction(adjustmentPendingPayment);
    holders.add(holder1);
    holders.add(holder2);
    assertDoesNotThrow(() -> fundAvailabilityValidator.validate(holders));
}
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) Budget(org.folio.rest.acq.model.finance.Budget) AwaitingPayment(org.folio.rest.acq.model.finance.AwaitingPayment) Test(org.junit.jupiter.api.Test)

Aggregations

Budget (org.folio.rest.acq.model.finance.Budget)26 Test (org.junit.jupiter.api.Test)19 ArrayList (java.util.ArrayList)17 Fund (org.folio.rest.acq.model.finance.Fund)14 Transaction (org.folio.rest.acq.model.finance.Transaction)12 FundDistribution (org.folio.rest.jaxrs.model.FundDistribution)10 InvoiceWorkflowDataHolder (org.folio.models.InvoiceWorkflowDataHolder)9 EncumbranceRelationsHolder (org.folio.models.EncumbranceRelationsHolder)8 FiscalYear (org.folio.rest.acq.model.finance.FiscalYear)7 Error (org.folio.rest.jaxrs.model.Error)7 List (java.util.List)6 HttpException (org.folio.invoices.rest.exceptions.HttpException)6 JsonObject (io.vertx.core.json.JsonObject)4 Collections (java.util.Collections)4 Collectors.toList (java.util.stream.Collectors.toList)4 BudgetCollection (org.folio.rest.acq.model.finance.BudgetCollection)4 Ledger (org.folio.rest.acq.model.finance.Ledger)4 Invoice (org.folio.rest.jaxrs.model.Invoice)4 InvoiceLine (org.folio.rest.jaxrs.model.InvoiceLine)4 HttpException (org.folio.rest.core.exceptions.HttpException)3