use of org.folio.rest.acq.model.finance.Transaction 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());
}
use of org.folio.rest.acq.model.finance.Transaction in project mod-invoice by folio-org.
the class InvoicesApiTest method verifyTransitionToApproved.
private void verifyTransitionToApproved(Voucher voucherCreated, List<InvoiceLine> invoiceLines, Invoice invoice, int createdVoucherLines) {
List<JsonObject> invoiceLinesSearches = serverRqRs.get(INVOICE_LINES, HttpMethod.GET);
List<JsonObject> invoiceLinesUpdates = serverRqRs.get(INVOICE_LINES, HttpMethod.PUT);
List<JsonObject> voucherLinesCreated = serverRqRs.get(VOUCHER_LINES, HttpMethod.POST);
List<JsonObject> fundsSearches = serverRqRs.get(FUNDS, HttpMethod.GET);
List<JsonObject> invoiceUpdates = serverRqRs.get(INVOICES, HttpMethod.PUT);
List<JsonObject> transactionSummariesCreated = serverRqRs.get(INVOICE_TRANSACTION_SUMMARIES, HttpMethod.POST);
List<JsonObject> pendingPaymentCreated = Optional.ofNullable(serverRqRs.get(FINANCE_PENDING_PAYMENTS, HttpMethod.POST)).orElse(emptyList());
assertThat(invoiceLinesSearches, notNullValue());
assertThat(invoiceLinesUpdates, notNullValue());
assertThat(fundsSearches, notNullValue());
assertThat(voucherLinesCreated, notNullValue());
assertThat(invoiceUpdates, notNullValue());
assertThat(transactionSummariesCreated, notNullValue());
assertThat(invoiceLinesSearches, hasSize(invoiceLines.size() / MAX_IDS_FOR_GET_RQ + 1));
List<InvoiceLine> linesWithUpdatedStatus = invoiceLinesUpdates.stream().map(entries -> entries.mapTo(InvoiceLine.class)).filter(invoiceLine -> invoiceLine.getInvoiceLineStatus() == InvoiceLine.InvoiceLineStatus.APPROVED).collect(toList());
assertThat(linesWithUpdatedStatus, hasSize(invoiceLines.size()));
assertThat(voucherLinesCreated, hasSize(createdVoucherLines));
InvoiceTransactionSummary transactionSummary = transactionSummariesCreated.get(0).mapTo(InvoiceTransactionSummary.class);
Invoice invoiceUpdate = invoiceUpdates.get(0).mapTo(Invoice.class);
List<VoucherLine> voucherLines = voucherLinesCreated.stream().map(json -> json.mapTo(VoucherLine.class)).collect(Collectors.toList());
assertThat(Invoice.Status.APPROVED, equalTo(invoiceUpdate.getStatus()));
assertThat(invoiceUpdate.getVoucherNumber(), equalTo(voucherCreated.getVoucherNumber()));
assertThat(invoiceUpdate.getId(), equalTo(voucherCreated.getInvoiceId()));
assertThat(invoiceUpdate.getCurrency(), equalTo(voucherCreated.getInvoiceCurrency()));
// assertThat(HelperUtils.getInvoiceExchangeRateProvider().getExchangeRate(voucherCreated.getInvoiceCurrency(), voucherCreated.getSystemCurrency()).getFactor().doubleValue(), equalTo(voucherCreated.getExchangeRate()));
assertThat(voucherCreated.getAccountingCode(), equalTo(invoiceUpdate.getAccountingCode()));
assertThat(voucherCreated.getExportToAccounting(), is(false));
assertThat(Voucher.Status.AWAITING_PAYMENT, equalTo(voucherCreated.getStatus()));
assertThat(Voucher.Type.VOUCHER, equalTo(voucherCreated.getType()));
int paymentCreditNumber = invoiceLines.stream().filter(invoiceLine -> invoiceLine.getTotal() >= 0).mapToInt(line -> line.getFundDistributions().size()).sum();
paymentCreditNumber += invoice.getAdjustments().stream().mapToInt(adj -> adj.getFundDistributions().size()).sum();
assertThat(pendingPaymentCreated, hasSize(paymentCreditNumber));
List<Transaction> pendingPayments = pendingPaymentCreated.stream().map(entries -> entries.mapTo(Transaction.class)).collect(toList());
assertThat(pendingPayments, Every.everyItem(hasProperty("sourceInvoiceId", is(invoice.getId()))));
assertThat(transactionSummary.getNumPendingPayments(), is(paymentCreditNumber));
assertThat(transactionSummary.getNumPaymentsCredits(), is(paymentCreditNumber));
assertThat(calculateVoucherAmount(voucherCreated, voucherLines), equalTo(voucherCreated.getAmount()));
assertThat(createdVoucherLines, equalTo(voucherLinesCreated.size()));
invoiceLines.forEach(invoiceLine -> calculateInvoiceLineTotals(invoiceLine, invoiceUpdate));
voucherLines.forEach(voucherLine -> {
assertThat(voucherCreated.getId(), equalTo(voucherLine.getVoucherId()));
assertThat(voucherLine.getFundDistributions(), allOf(Every.everyItem(hasProperty("distributionType", is(AMOUNT))), Every.everyItem(hasProperty("code"))));
assertThat(calculateVoucherLineAmount(voucherLine.getFundDistributions(), voucherCreated), equalTo(voucherLine.getAmount()));
assertThat(voucherLine.getFundDistributions().stream().filter(fundDistribution -> Objects.nonNull(fundDistribution.getInvoiceLineId())).map(FundDistribution::getInvoiceLineId).distinct().collect(Collectors.toList()), hasSize(voucherLine.getSourceIds().size()));
});
}
use of org.folio.rest.acq.model.finance.Transaction in project mod-invoice by folio-org.
the class InvoicesApiTest method testInvoiceTransitionApprovedWithOddNumberOfPennies.
@Test
void testInvoiceTransitionApprovedWithOddNumberOfPennies() {
logger.info("=== Test transition invoice to Approved with odd number of pennies ===");
InvoiceLine invoiceLine = getMockAsJson(INVOICE_LINES_LIST_PATH).mapTo(InvoiceLineCollection.class).getInvoiceLines().get(0);
Invoice invoice = getMockAsJson(OPEN_INVOICE_SAMPLE_PATH).mapTo(Invoice.class);
invoice.getAdjustments().clear();
invoice.getAdjustments().add(createAdjustment(Prorate.BY_LINE, Type.AMOUNT, 4d));
String id = invoice.getId();
invoiceLine.setId(UUID.randomUUID().toString());
invoiceLine.setInvoiceId(id);
var conversionFactor = 3d;
invoice.setExchangeRate(conversionFactor);
List<FundDistribution> fundDistrList = new ArrayList<>();
invoiceLine.setSubTotal(20.01d);
invoiceLine.getAdjustments().clear();
fundDistrList.add(new FundDistribution().withFundId("1d1574f1-9196-4a57-8d1f-3b2e4309eb81").withDistributionType(PERCENTAGE).withValue(50d));
fundDistrList.add(new FundDistribution().withFundId("55f48dc6-efa7-4cfe-bc7c-4786efe493e3").withDistributionType(PERCENTAGE).withValue(50d));
invoiceLine.setFundDistributions(fundDistrList);
addMockEntry(INVOICE_LINES, JsonObject.mapFrom(invoiceLine));
invoice.setStatus(Invoice.Status.APPROVED);
String jsonBody = JsonObject.mapFrom(invoice).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);
// Verify that expected number of external calls made
assertThat(getInvoiceRetrievals(), hasSize(1));
assertThat(getInvoiceLineSearches(), hasSize(1));
Invoice updatedInvoice = serverRqRs.get(INVOICES, HttpMethod.PUT).get(0).mapTo(Invoice.class);
List<JsonObject> vouchersCreated = serverRqRs.get(VOUCHERS_STORAGE, HttpMethod.POST);
assertThat(vouchersCreated, notNullValue());
assertThat(vouchersCreated, hasSize(1));
Voucher voucherCreated = vouchersCreated.get(0).mapTo(Voucher.class);
assertThat(voucherCreated.getVoucherNumber(), equalTo(TEST_PREFIX + VOUCHER_NUMBER_VALUE));
assertThat(voucherCreated.getSystemCurrency(), equalTo(DEFAULT_SYSTEM_CURRENCY));
verifyTransitionToApproved(voucherCreated, Collections.singletonList(invoiceLine), updatedInvoice, 2);
checkVoucherAcqUnitIdsList(voucherCreated, invoice);
List<Transaction> pendingPayments = serverRqRs.get(FINANCE_PENDING_PAYMENTS, HttpMethod.POST).stream().map(transaction -> transaction.mapTo(Transaction.class)).collect(toList());
Double transactionsAmount = pendingPayments.stream().map(tr -> Money.of(tr.getAmount(), tr.getCurrency())).reduce(Money::add).get().getNumber().doubleValue();
InvoiceLine invLineWithRecalculatedTotals = serverRqRs.get(INVOICE_LINES, HttpMethod.PUT).get(0).mapTo(InvoiceLine.class);
assertEquals(invLineWithRecalculatedTotals.getTotal(), transactionsAmount);
// check voucher totals
Voucher voucher = serverRqRs.get(VOUCHERS_STORAGE, HttpMethod.POST).stream().map(transaction -> transaction.mapTo(Voucher.class)).collect(toList()).get(0);
List<VoucherLine> voucherLines = serverRqRs.get(VOUCHER_LINES, HttpMethod.POST).stream().map(transaction -> transaction.mapTo(VoucherLine.class)).collect(toList());
Double voucherLinesTotal = voucherLines.stream().map(vLine -> BigDecimal.valueOf(vLine.getAmount())).reduce(BigDecimal::add).get().doubleValue();
var expectedVoucherAmount = Money.of(invLineWithRecalculatedTotals.getTotal(), invoice.getCurrency()).multiply(conversionFactor).getNumber().doubleValue();
assertEquals(expectedVoucherAmount, voucher.getAmount());
assertEquals(expectedVoucherAmount, voucherLinesTotal);
}
use of org.folio.rest.acq.model.finance.Transaction in project mod-invoice by folio-org.
the class MockServer method handleGetFinanceTransactions.
private void handleGetFinanceTransactions(RoutingContext ctx) {
logger.info("handleGetFinanceTransactions got: " + ctx.request().path());
String query = StringUtils.trimToEmpty(ctx.request().getParam("query"));
if (query.contains(BAD_QUERY)) {
serverResponse(ctx, 400, APPLICATION_JSON, Response.Status.BAD_REQUEST.getReasonPhrase());
} else {
List<Transaction> transactions = getMockEntries(FINANCE_TRANSACTIONS, Transaction.class).orElse(Collections.emptyList());
TransactionCollection transactionCollection = new TransactionCollection().withTransactions(transactions);
JsonObject data = JsonObject.mapFrom(transactionCollection.withTotalRecords(transactions.size()));
addServerRqRsData(HttpMethod.GET, FINANCE_TRANSACTIONS, data);
serverResponse(ctx, 200, APPLICATION_JSON, data.encodePrettily());
}
}
use of org.folio.rest.acq.model.finance.Transaction in project mod-invoice by folio-org.
the class InvoiceCancelService method cancelTransactions.
private CompletableFuture<Void> cancelTransactions(String invoiceId, List<Transaction> transactions, RequestContext requestContext) {
if (transactions.size() == 0)
return completedFuture(null);
transactions.forEach(tr -> tr.setInvoiceCancelled(true));
InvoiceTransactionSummary summary = buildInvoiceTransactionsSummary(invoiceId, transactions);
return invoiceTransactionSummaryService.updateInvoiceTransactionSummary(summary, requestContext).thenCompose(s -> baseTransactionService.updateTransactions(transactions, requestContext)).exceptionally(t -> {
logger.error("Failed to cancel transactions for invoice with id {}", invoiceId, t);
List<Parameter> parameters = Collections.singletonList(new Parameter().withKey("invoiceId").withValue(invoiceId));
throw new HttpException(500, CANCEL_TRANSACTIONS_ERROR.toError().withParameters(parameters));
});
}
Aggregations