Skip to content

Commit

Permalink
[MODINVOICE-514] - Uninformative error in invoice payment due to user…
Browse files Browse the repository at this point in the history
…not linked to the Purchase Order's acquisition unit (#457)

[MODINVOICE-514] - Uninformative error in invoice payment due to user not linked to the Purchase Order's acquisition unit
  • Loading branch information
imerabishvili authored Dec 11, 2023
1 parent 52138c7 commit 047bdb9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/main/java/org/folio/invoices/utils/ErrorCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum ErrorCodes {
CANNOT_DELETE_INVOICE_LINE("cannotDeleteInvoiceLine", "Cannot delete invoice-line because invoice record associated with invoice-line not found"),
INVALID_INVOICE_TRANSITION_ON_PAID_STATUS("invalidInvoiceStatusTransitionOnPaidStatus", "Cannot transition invoice to any other statuses when it is in Paid status"),
PO_LINE_UPDATE_FAILURE("poLineUpdateFailure", "One or more purchase order line record(s) cannot be updated"),
USER_NOT_A_MEMBER_OF_THE_ACQ("userNotAMemberOfTheAcq", "User is not a member of the specified acquisitions group - operation is restricted"),
VOUCHER_NOT_FOUND("voucherNotFound", "The voucher record is not found"),
FUND_DISTRIBUTIONS_NOT_PRESENT("fundDistributionsNotPresent", "At least one fund distribution should present for every associated invoice line"),
ACCOUNTING_CODE_NOT_PRESENT("accountingCodeNotPresent", "Invoice can not be approved, because it requires an accounting code to be export to accounting"),
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/org/folio/services/order/OrderLineService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.folio.invoices.utils.ErrorCodes.PO_LINE_NOT_FOUND;
import static org.folio.invoices.utils.ErrorCodes.PO_LINE_UPDATE_FAILURE;
import static org.folio.invoices.utils.ErrorCodes.USER_NOT_A_MEMBER_OF_THE_ACQ;
import static org.folio.invoices.utils.ResourcePathResolver.ORDER_LINES;
import static org.folio.invoices.utils.ResourcePathResolver.resourcesPath;

Expand All @@ -18,6 +19,7 @@
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;
import org.folio.rest.jaxrs.model.Parameter;
import org.folio.utils.ExceptionUtil;

import io.vertx.core.Future;

Expand Down Expand Up @@ -57,8 +59,12 @@ public Future<Void> updatePoLine(CompositePoLine poLine, RequestContext requestC
public Future<Void> updateCompositePoLines(List<CompositePoLine> poLines, RequestContext requestContext) {
var futures = poLines.stream()
.map(poLine -> updatePoLine(poLine, requestContext)
.recover(t -> {
throw new HttpException(400, PO_LINE_UPDATE_FAILURE.toError());
.recover(cause -> {
if (ExceptionUtil.matches(cause, USER_NOT_A_MEMBER_OF_THE_ACQ)) {
throw new HttpException(403, USER_NOT_A_MEMBER_OF_THE_ACQ.toError());
} else {
throw new HttpException(400, PO_LINE_UPDATE_FAILURE.toError());
}
}))
.collect(Collectors.toList());
return GenericCompositeFuture.join(futures).mapEmpty();
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/org/folio/utils/ExceptionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.folio.utils;

import org.folio.invoices.rest.exceptions.HttpException;
import org.folio.invoices.utils.ErrorCodes;

public final class ExceptionUtil {

private ExceptionUtil() {
}

public static boolean matches(Throwable cause, ErrorCodes errorCode) {
if (cause instanceof HttpException httpException) {
var errors = httpException.getErrors();
return errors != null && errors.getErrors() != null && errors.getErrors()
.stream()
.anyMatch(error -> errorCode.getCode().equals(error.getCode()));
}
return false;
}

}
30 changes: 30 additions & 0 deletions src/test/java/org/folio/services/order/OrderServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.folio.services.order;

import static io.vertx.core.Future.failedFuture;
import static io.vertx.core.Future.succeededFuture;
import static org.folio.invoices.utils.ErrorCodes.USER_NOT_A_MEMBER_OF_THE_ACQ;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
Expand All @@ -11,12 +14,15 @@
import java.util.List;
import java.util.UUID;

import org.folio.HttpStatus;
import org.folio.invoices.rest.exceptions.HttpException;
import org.folio.rest.acq.model.orders.CompositePoLine;
import org.folio.rest.acq.model.orders.OrderInvoiceRelationship;
import org.folio.rest.acq.model.orders.OrderInvoiceRelationshipCollection;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;
import org.folio.rest.jaxrs.model.Error;
import org.folio.services.invoice.InvoiceLineService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -25,6 +31,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import io.vertx.core.Future;
import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext;

Expand All @@ -40,6 +47,8 @@ public class OrderServiceTest {
private RequestContext requestContextMock;
@Mock
private OrderLineService orderLineService;
@InjectMocks
private OrderLineService orderLineServiceInject;

@BeforeEach
public void initMocks() {
Expand Down Expand Up @@ -127,4 +136,25 @@ void shouldNotDeleteOrderInvoiceRelationshipByInvoiceIdIfRelationNoExist() {

verify(restClient, times(0)).delete(any(RequestEntry.class), eq(requestContextMock));
}

@Test
void shouldRethrowUserNotAMemberOfTheAcq(VertxTestContext vertxTestContext) {
// given
doReturn(failedFuture(new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_NOT_A_MEMBER_OF_THE_ACQ.toError())))
.when(restClient).put(any(RequestEntry.class), any(CompositePoLine.class), eq(requestContextMock));

// when
Future<Void> future = orderLineServiceInject.updateCompositePoLines(List.of(new CompositePoLine()), requestContextMock);

// then
vertxTestContext.assertFailure(future)
.onComplete(result -> {
HttpException httpException = (HttpException) result.cause();
assertEquals(HttpStatus.HTTP_FORBIDDEN.toInt(), httpException.getCode());
Error error = httpException.getErrors().getErrors().get(0);
assertEquals(USER_NOT_A_MEMBER_OF_THE_ACQ.getCode(), error.getCode());
vertxTestContext.completeNow();
});
}

}

0 comments on commit 047bdb9

Please sign in to comment.