Skip to content

Commit

Permalink
Merge branch 'master' into spring-6
Browse files Browse the repository at this point in the history
  • Loading branch information
julianladisch authored Aug 19, 2024
2 parents 8532ed2 + 03a5866 commit 7a4a1e2
Show file tree
Hide file tree
Showing 15 changed files with 493 additions and 99 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 5.9.0 - Unreleased
* [MODINVOICE-543](https://folio-org.atlassian.net/browse/MODINVOICE-543) - EDIFACT invoice lines load out of order
* [MODINVOICE-548](https://folio-org.atlassian.net/browse/MODINVOICE-548) - Provide fund code population when fund distributions mapping is specified

## 5.8.0 - Released (Quesnelia R1 2024)
The focus of this release was to fix bugs and make improvement in transaction call and error codes
Expand Down
3 changes: 2 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"invoice-storage.invoice-number.get",
"acquisitions-units-storage.units.collection.get",
"acquisitions-units-storage.memberships.collection.get",
"organizations-storage.organizations.item.get"
"organizations-storage.organizations.item.get",
"finance.ledgers.current-fiscal-year.item.get"
]
},
{
Expand Down
12 changes: 2 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
<mod-configuration-client.version>5.10.0</mod-configuration-client.version>
<spring.version>6.1.12</spring.version>
<folio-di-support.version>2.1.0</folio-di-support.version>
<jackson-bom.version>2.13.4</jackson-bom.version>
<assertj-core.version>3.25.3</assertj-core.version>
<aspectj.version>1.9.21.1</aspectj.version>
<log4j.version>2.23.0</log4j.version>
Expand Down Expand Up @@ -181,7 +180,7 @@
<dependency>
<groupId>org.folio</groupId>
<artifactId>data-import-processing-core</artifactId>
<version>4.2.0</version>
<version>4.3.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.folio</groupId>
Expand All @@ -192,7 +191,7 @@
<dependency>
<groupId>org.folio</groupId>
<artifactId>mod-di-converter-storage-client</artifactId>
<version>2.1.8</version>
<version>2.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
Expand Down Expand Up @@ -763,13 +762,6 @@
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion ramls/acq-models
Submodule acq-models updated 43 files
+1 −0 mod-finance/examples/budget.sample
+1 −0 mod-finance/examples/budget_expense_class_totals.sample
+1 −0 mod-finance/examples/budget_expense_class_totals_collection.sample
+1 −0 mod-finance/examples/group_expense_class_totals.sample
+1 −0 mod-finance/examples/group_expense_class_totals_collection.sample
+1 −0 mod-finance/examples/group_fiscal_year_summary.sample
+2 −0 mod-finance/examples/group_fiscal_year_summary_collection.sample
+1 −0 mod-finance/examples/ledger.sample
+1 −0 mod-finance/examples/ledger_fiscal_year_rollover_budget.sample
+1 −0 mod-finance/examples/ledger_fiscal_year_rollover_budget_collection.sample
+1 −0 mod-finance/examples/shared_budget.sample
+1 −0 mod-finance/examples/transaction_encumbrance.sample
+5 −0 mod-finance/schemas/budget.json
+8 −0 mod-finance/schemas/budget_expense_class_totals.json
+5 −0 mod-finance/schemas/encumbrance.json
+5 −0 mod-finance/schemas/fiscal_year_financial_summary.json
+8 −0 mod-finance/schemas/group_expense_class_totals.json
+5 −0 mod-finance/schemas/group_fiscal_year_summary.json
+5 −0 mod-finance/schemas/ledger.json
+5 −0 mod-finance/schemas/ledger_fiscal_year_rollover_budget.json
+5 −0 mod-finance/schemas/shared_budget.json
+2 −1 mod-orders-storage/examples/piece_collection.sample
+1 −0 mod-orders-storage/examples/piece_post.sample
+1 −1 mod-orders-storage/examples/po_line_collection.sample
+1 −1 mod-orders-storage/examples/po_line_get.sample
+1 −1 mod-orders-storage/examples/po_line_post.sample
+5 −1 mod-orders-storage/examples/routing_list_collection.sample
+5 −1 mod-orders-storage/examples/routing_list_get.sample
+4 −0 mod-orders-storage/examples/title_collection.sample
+4 −0 mod-orders-storage/examples/title_get.sample
+4 −0 mod-orders-storage/examples/title_post.sample
+1 −1 mod-orders-storage/schemas/details.json
+2 −1 mod-orders-storage/schemas/piece.json
+5 −1 mod-orders-storage/schemas/routing_list.json
+7 −0 mod-orders-storage/schemas/title.json
+16 −0 mod-orders/examples/bindPiecesCollection.sample
+8 −0 mod-orders/examples/bindPiecesResult.sample
+1 −1 mod-orders/examples/composite_po_line.sample
+1 −0 mod-orders/examples/composite_purchase_order.sample
+71 −0 mod-orders/schemas/bindPiecesCollection.json
+31 −0 mod-orders/schemas/bindPiecesResult.json
+5 −0 mod-orders/schemas/composite_purchase_order.json
+10 −0 mod-orders/schemas/requestsAction.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import static io.vertx.core.Future.succeededFuture;
import static java.lang.String.format;
import static one.util.streamex.StreamEx.ofSubLists;
import static org.apache.commons.collections4.CollectionUtils.isEmpty;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.folio.ActionProfile.Action.CREATE;
import static org.folio.ActionProfile.FolioRecord.INVOICE;
import static org.folio.DataImportEventTypes.DI_INVOICE_CREATED;
Expand All @@ -13,7 +16,7 @@
import static org.folio.invoices.utils.ResourcePathResolver.resourcesPath;
import static org.folio.rest.jaxrs.model.EntityType.EDIFACT_INVOICE;
import static org.folio.rest.jaxrs.model.InvoiceLine.InvoiceLineStatus.OPEN;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.ACTION_PROFILE;
import static org.folio.rest.jaxrs.model.ProfileType.ACTION_PROFILE;

import io.vertx.core.Future;
import io.vertx.core.Vertx;
Expand Down Expand Up @@ -44,6 +47,7 @@
import org.folio.ParsedRecord;
import org.folio.Record;
import org.folio.dataimport.utils.DataImportUtils;
import org.folio.dbschema.ObjectMapperTool;
import org.folio.domain.relationship.RecordToEntity;
import org.folio.invoices.rest.exceptions.HttpException;
import org.folio.invoices.utils.HelperUtils;
Expand All @@ -64,6 +68,7 @@
import org.folio.rest.jaxrs.model.Invoice;
import org.folio.rest.jaxrs.model.InvoiceLine;
import org.folio.rest.jaxrs.model.InvoiceLineCollection;
import org.folio.rest.jaxrs.model.ProfileSnapshotWrapper;
import org.folio.services.invoice.IdStorageService;

public class CreateInvoiceEventHandler implements EventHandler {
Expand All @@ -83,6 +88,8 @@ public class CreateInvoiceEventHandler implements EventHandler {
private static final String INVOICE_LINES_RULE_NAME = "invoiceLines";
private static final String REFERENCE_NUMBERS_RULE_NAME = "referenceNumbers";
private static final String REF_NUMBER_RULE_NAME = "refNumber";
private static final String FUND_DISTRIBUTIONS_RULE_NAME = "fundDistributions";
private static final String FUND_ID_RULE_NAME = "fundId";
private static final String POL_NUMBER_KEY = "POL_NUMBER_%s";
private static final String POL_EXPENSE_CLASS_KEY = "POL_EXPENSE_CLASS_%s";
private static final String POL_FUND_DISTRIBUTIONS_KEY = "POL_FUND_DISTRIBUTIONS_%s";
Expand All @@ -93,6 +100,8 @@ public class CreateInvoiceEventHandler implements EventHandler {
private final RestClient restClient;
private final IdStorageService idStorageService;
public static final String UNIQUE_KEY_CONSTRAINT_ERROR = "duplicate key value violates unique constraint";
private static final char LEFT_BRACKET = '(';
private static final char RIGHT_BRACKET = ')';

public CreateInvoiceEventHandler(RestClient restClient, IdStorageService idStorageService) {
this.restClient = restClient;
Expand Down Expand Up @@ -372,9 +381,45 @@ private List<InvoiceLine> prepareInvoiceLinesToSave(String invoiceId, DataImport
.collect(Collectors.toList());

linkInvoiceLinesToPoLines(invoiceLines, associatedPoLines);
ensureFundCode(invoiceLines, dataImportEventPayload);
return invoiceLines;
}

private void ensureFundCode(List<InvoiceLine> invoiceLines, DataImportEventPayload dataImportEventPayload) {
if (invoiceLines.stream().allMatch(line -> isEmpty(line.getFundDistributions()))) {
return;
}

Map<String, String> idToFundName = extractFundsData(dataImportEventPayload);
if (!idToFundName.isEmpty()) {
invoiceLines.stream()
.filter(invoiceLine -> isNotEmpty(invoiceLine.getFundDistributions()))
.forEach(invoiceLine -> invoiceLine.getFundDistributions().stream()
.filter(fundDistribution -> isNotEmpty(fundDistribution.getFundId()) && isEmpty(fundDistribution.getCode()))
.forEach(fundDistribution -> populateFundCode(fundDistribution, idToFundName)));
}
}

private Map<String, String> extractFundsData(DataImportEventPayload dataImportEventPayload) {
ProfileSnapshotWrapper mappingProfileWrapper = dataImportEventPayload.getCurrentNode();
MappingProfile mappingProfile = ObjectMapperTool.getMapper().convertValue(mappingProfileWrapper.getContent(), MappingProfile.class);

return mappingProfile.getMappingDetails().getMappingFields().stream()
.filter(mappingRule -> INVOICE_LINES_RULE_NAME.equals(mappingRule.getName()) && !mappingRule.getSubfields().isEmpty())
.flatMap(mappingRule -> mappingRule.getSubfields().get(0).getFields().stream())
.filter(mappingRule -> FUND_DISTRIBUTIONS_RULE_NAME.equals(mappingRule.getName()) && !mappingRule.getSubfields().isEmpty())
.flatMap(mappingRule -> mappingRule.getSubfields().get(0).getFields().stream())
.filter(mappingRule -> FUND_ID_RULE_NAME.equals(mappingRule.getName()))
.map(mappingRule -> ((Map<String, String>) mappingRule.getAcceptedValues()))
.findAny()
.orElse(Collections.emptyMap());
}

private void populateFundCode(org.folio.rest.jaxrs.model.FundDistribution fundDistribution, Map<String, String> idToFundName) {
String fundName = idToFundName.get(fundDistribution.getFundId());
fundDistribution.setCode(fundName.substring(fundName.lastIndexOf(LEFT_BRACKET) + 1, fundName.lastIndexOf(RIGHT_BRACKET)));
}

private void linkInvoiceLinesToPoLines(List<InvoiceLine> invoiceLines, Map<Integer, PoLine> associatedPoLines) {
for (int i = 0; i < invoiceLines.size(); i++) {
if (associatedPoLines.get(i + 1) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

public class InvoiceCancelService {
private static final String PO_LINES_WITH_RIGHT_PAYMENT_STATUS_QUERY =
"paymentStatus==(\"Awaiting Payment\" OR \"Partially Paid\" OR \"Fully Paid\" OR \"Ongoing\")";
"paymentStatus==(\"Awaiting Payment\" OR \"Partially Paid\" OR \"Fully Paid\" OR \"Ongoing\" OR \"Payment Not Required\")";
private static final String PAYMENT_STATUS_PAID_QUERY = "paymentStatus==(\"Fully Paid\" OR \"Partially Paid\")";
private static final String OPEN_ORDERS_QUERY = "workflowStatus==\"Open\"";
private static final String AND = " AND ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import static java.util.stream.Collectors.toList;
import static org.folio.invoices.utils.HelperUtils.collectResultsOnSuccess;
import static org.folio.rest.acq.model.orders.CompositePoLine.PaymentStatus.ONGOING;
import static org.folio.rest.acq.model.orders.CompositePoLine.PaymentStatus.PAYMENT_NOT_REQUIRED;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -43,6 +47,8 @@ public class InvoicePaymentService {
private CurrentFiscalYearService currentFiscalYearService;

public static final String INVOICE_LINE_MUST_HAVE_FUND = "The invoice line must contain the fund for payment";
protected static final Set<CompositePoLine.PaymentStatus> PO_LINE_PAYMENT_IGNORED_STATUSES =
EnumSet.of(ONGOING, PAYMENT_NOT_REQUIRED);

/**
* Handles transition of given invoice to PAID status.
Expand Down Expand Up @@ -154,6 +160,6 @@ private boolean isAnyInvoiceLineReleaseEncumbrance(List<InvoiceLine> invoiceLine
boolean isPaymentStatusUpdateRequired(Map<CompositePoLine, CompositePoLine.PaymentStatus> compositePoLinesWithStatus, CompositePoLine compositePoLine) {
CompositePoLine.PaymentStatus newPaymentStatus = compositePoLinesWithStatus.get(compositePoLine);
return (!newPaymentStatus.equals(compositePoLine.getPaymentStatus()) &&
!compositePoLine.getPaymentStatus().equals(CompositePoLine.PaymentStatus.ONGOING));
!PO_LINE_PAYMENT_IGNORED_STATUSES.contains(compositePoLine.getPaymentStatus()));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.folio.services.validator;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static org.folio.invoices.utils.ErrorCodes.FUND_CANNOT_BE_PAID;
import static org.folio.invoices.utils.ResourcePathResolver.FUNDS;

Expand Down Expand Up @@ -48,12 +47,12 @@ public void validate(List<InvoiceWorkflowDataHolder> dataHolders) {
})
.map(Map.Entry::getKey)
.map(Budget::getFundId)
.collect(toList());
.toList();

if (!failedBudgetIds.isEmpty()) {
Parameter parameter = new Parameter().withKey(FUNDS)
.withValue(failedBudgetIds.stream().map(fundHoldersMap::get)
.collect(toList()).toString());
.toList().toString());
throw new HttpException(422, FUND_CANNOT_BE_PAID.toError()
.withParameters(Collections.singletonList(parameter)));
}
Expand All @@ -77,17 +76,25 @@ private MonetaryAmount calculateTotalExpendedAmount(List<InvoiceWorkflowDataHold
.orElseGet(() -> Money.zero(currency));
}

private boolean isRemainingAmountExceed(Budget budget, MonetaryAmount totalExpendedAmount) {
// [remaining amount we can expend] = (totalFunding * allowableExpenditure) - expended
// where expended = awaitingPayment + expenditure
CurrencyUnit currency = totalExpendedAmount.getCurrency();
Money totalFundings = Money.of(budget.getTotalFunding(), currency);
Money expended = Money.of(budget.getAwaitingPayment(), currency)
.add(Money.of(budget.getExpenditures(), currency));
BigDecimal allowableExpenditures = BigDecimal.valueOf(budget.getAllowableExpenditure())
.movePointLeft(2);
Money totalAmountCanBeExpended = totalFundings.multiply(allowableExpenditures);
Money afterApproveExpended = expended.add(totalExpendedAmount);
/**
* Method is following these formulas <br>
* afterApproveExpended [remaining amount we can expend] = expended - credited + awaitingPayment + additionalAmountToExpend <br>
* totalAmountCanBeExpended = totalFunding * allowableExpenditure
* @param additionalAmountToExpend additionalAmountToExpend
* @return boolean afterApproveExpended > totalAmountCanBeExpended
*/
private boolean isRemainingAmountExceed(Budget budget, MonetaryAmount additionalAmountToExpend) {
CurrencyUnit currency = additionalAmountToExpend.getCurrency();

Money totalFunding = Money.of(budget.getTotalFunding(), currency);
Money awaitingPayment = Money.of(budget.getAwaitingPayment(), currency);
Money expended = Money.of(budget.getExpenditures(), currency);
Money credited = Money.of(budget.getCredits(), currency);
BigDecimal allowableExpenditures = BigDecimal.valueOf(budget.getAllowableExpenditure()).movePointLeft(2);

Money totalAmountCanBeExpended = totalFunding.multiply(allowableExpenditures);
Money afterApproveExpended = expended.subtract(credited).add(awaitingPayment).add(additionalAmountToExpend);

return afterApproveExpended.isGreaterThan(totalAmountCanBeExpended);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.folio.dataimport.cache;

import static org.folio.rest.impl.MockServer.addMockEntry;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.ACTION_PROFILE;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.JOB_PROFILE;
import static org.folio.rest.jaxrs.model.ProfileType.ACTION_PROFILE;
import static org.folio.rest.jaxrs.model.ProfileType.JOB_PROFILE;

import java.util.List;
import java.util.Map;
Expand Down
Loading

0 comments on commit 7a4a1e2

Please sign in to comment.