From 2073932b06726b86f3ff30c12f66922ee75d1039 Mon Sep 17 00:00:00 2001 From: imerabishvili <144257054+imerabishvili@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:18:55 +0400 Subject: [PATCH] [MODORDERS-983] - Receiving a piece on a closed/cancelled PO will reopen the PO (#821) --- .../helper/CheckinReceivePiecesHelper.java | 10 +- .../handlers/ReceiptStatusConsistency.java | 26 ++--- .../rest/impl/CheckinReceivingApiTest.java | 48 ++++++++ .../1196fcd9-7607-447d-ae85-6e91883d7e4f.json | 102 +++++++++++++++++ .../mockdata/lines/po_line_collection.json | 104 +++++++++++++++++- ...-06a95f03-eb00-4248-9f2e-2bd05957ff05.json | 13 +++ .../pieces/pieceRecordsCollection.json | 10 +- .../receiving/receive-physical-cancelled.json | 20 ++++ 8 files changed, 314 insertions(+), 19 deletions(-) create mode 100644 src/test/resources/mockdata/lines/1196fcd9-7607-447d-ae85-6e91883d7e4f.json create mode 100644 src/test/resources/mockdata/pieces/pieceRecords-06a95f03-eb00-4248-9f2e-2bd05957ff05.json create mode 100644 src/test/resources/mockdata/receiving/receive-physical-cancelled.json diff --git a/src/main/java/org/folio/helper/CheckinReceivePiecesHelper.java b/src/main/java/org/folio/helper/CheckinReceivePiecesHelper.java index aa629cb54..d3f81cfc2 100644 --- a/src/main/java/org/folio/helper/CheckinReceivePiecesHelper.java +++ b/src/main/java/org/folio/helper/CheckinReceivePiecesHelper.java @@ -21,6 +21,7 @@ import static org.folio.rest.core.exceptions.ErrorCodes.PIECE_UPDATE_FAILED; import static org.folio.rest.core.exceptions.ErrorCodes.USER_HAS_NO_PERMISSIONS; import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.AWAITING_RECEIPT; +import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.CANCELLED; import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED; import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.ONGOING; import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.PARTIALLY_RECEIVED; @@ -299,11 +300,12 @@ protected Future>> updateOrderAndPoLinesStatus(Map> futures = new ArrayList<>(); for (PoLine poLine : poLines) { - if (poLine.getReceiptStatus() != ONGOING) { - List successfullyProcessedPieces = getSuccessfullyProcessedPieces(poLine.getId(), piecesGroupedByPoLine); - futures.add(calculatePoLineReceiptStatus(poLine, successfullyProcessedPieces, requestContext) - .compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext))); + if (poLine.getReceiptStatus() == CANCELLED || poLine.getReceiptStatus() == ONGOING) { + continue; } + List successfullyProcessedPieces = getSuccessfullyProcessedPieces(poLine.getId(), piecesGroupedByPoLine); + futures.add(calculatePoLineReceiptStatus(poLine, successfullyProcessedPieces, requestContext) + .compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext))); } return collectResultsOnSuccess(futures).map(updatedPoLines -> { diff --git a/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java b/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java index a3430a2cb..5c840f610 100644 --- a/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java +++ b/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java @@ -67,12 +67,13 @@ public void handle(Message message) { // 1. Get all pieces for poLineId getPieces(query, requestContext) .onSuccess(listOfPieces -> - // 2. Get PoLine for the poLineId which will be used to calculate PoLineReceiptStatus - purchaseOrderLineService.getOrderLineById(poLineIdUpdate, requestContext) - .map(poLine -> { - if (poLine.getReceiptStatus().equals(PoLine.ReceiptStatus.ONGOING)) { - promise.complete(); - } else { + // 2. Get PoLine for the poLineId which will be used to calculate PoLineReceiptStatus + purchaseOrderLineService.getOrderLineById(poLineIdUpdate, requestContext) + .map(poLine -> { + if (poLine.getReceiptStatus() == ReceiptStatus.CANCELLED || poLine.getReceiptStatus() == ReceiptStatus.ONGOING) { + promise.complete(); + return null; + } calculatePoLineReceiptStatus(poLine, listOfPieces) .compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext)) .map(updatedPoLineId -> { @@ -87,13 +88,12 @@ public void handle(Message message) { logger.error("The error updating poLine by id {}", poLineIdUpdate, e); promise.fail(e); }); - } - return null; - }) - .onFailure(e -> { - logger.error("The error getting poLine by id {}", poLineIdUpdate, e); - promise.fail(e); - })) + return null; + }) + .onFailure(e -> { + logger.error("The error getting poLine by id {}", poLineIdUpdate, e); + promise.fail(e); + })) .onFailure(e -> { logger.error("The error happened getting all pieces by poLine {}", poLineIdUpdate, e); promise.fail(e); diff --git a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java index ece371129..76c5e5f61 100644 --- a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java +++ b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java @@ -117,6 +117,7 @@ public class CheckinReceivingApiTest { private static final String ITEM_STATUS_NAME = "name"; private static final String ITEM_STATUS = "status"; private static final String COMPOSITE_POLINE_ONGOING_ID = "6e2b169a-ebeb-4c3c-a2f2-6233ff9c59ae"; + private static final String COMPOSITE_POLINE_CANCELED_ID = "1196fcd9-7607-447d-ae85-6e91883d7e4f"; private static boolean runningOnOwn; @@ -350,6 +351,53 @@ void testReceiveOngoingOrder() { verifyOrderStatusUpdateEvent(0); } + @Test + void testReceiveCanceledOrder() { + logger.info("=== Test POST Receive - Ongoing PO Lines"); + + CompositePoLine poLines = getMockAsJson(POLINES_COLLECTION).getJsonArray("poLines").getJsonObject(10).mapTo(CompositePoLine.class); + MockServer.addMockTitles(Collections.singletonList(poLines)); + + ReceivingCollection receivingRq = getMockAsJson(RECEIVING_RQ_MOCK_DATA_PATH + "receive-physical-cancelled.json").mapTo(ReceivingCollection.class); + receivingRq.getToBeReceived().get(0).setPoLineId(COMPOSITE_POLINE_CANCELED_ID); + + ReceivingResults results = verifyPostResponse(ORDERS_RECEIVING_ENDPOINT, JsonObject.mapFrom(receivingRq).encode(), + prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, 200).as(ReceivingResults.class); + + assertThat(results.getTotalRecords(), equalTo(receivingRq.getTotalRecords())); + + Map> pieceIdsByPol = verifyReceivingSuccessRs(results); + + List pieceSearches = getPieceSearches(); + List pieceUpdates = getPieceUpdates(); + List polSearches = getPoLineSearches(); + List polUpdates = getPoLineUpdates(); + + assertThat(pieceSearches, not(nullValue())); + assertThat(pieceUpdates, not(nullValue())); + + assertThat(polSearches, not(nullValue())); + + int expectedSearchRqQty = Math.floorDiv(receivingRq.getTotalRecords(), MAX_IDS_FOR_GET_RQ_15) + 1; + + // The piece searches should be made 1 time: 1st time to get all required piece records + assertThat(pieceSearches, hasSize(expectedSearchRqQty)); + assertThat(pieceUpdates, hasSize(receivingRq.getTotalRecords())); + assertThat(polSearches, hasSize(pieceIdsByPol.size())); + + // check no status updates were performed and POL remained canceled + assertThat(polUpdates, nullValue()); + polSearches.forEach(pol -> { + PoLine poLine = pol.mapTo(PoLineCollection.class).getPoLines().get(0); + assertThat(poLine.getCheckinItems(), is(false)); + assertThat(poLine.getReceiptStatus(), is(PoLine.ReceiptStatus.CANCELLED)); + assertThat(poLine.getReceiptDate(), is(notNullValue())); + }); + + // Verify no status updated for ongoing order + verifyOrderStatusUpdateEvent(0); + } + @Test void testPostCheckInLocationId() { diff --git a/src/test/resources/mockdata/lines/1196fcd9-7607-447d-ae85-6e91883d7e4f.json b/src/test/resources/mockdata/lines/1196fcd9-7607-447d-ae85-6e91883d7e4f.json new file mode 100644 index 000000000..a81a10ebf --- /dev/null +++ b/src/test/resources/mockdata/lines/1196fcd9-7607-447d-ae85-6e91883d7e4f.json @@ -0,0 +1,102 @@ +{ + "id": "1196fcd9-7607-447d-ae85-6e91883d7e4f", + "acquisitionMethod": "306489dd-0053-49ee-a068-c316444a8f55", + "alerts": [], + "cancellationRestriction": false, + "cancellationRestrictionNote": "ABCDEFGHIJKLMNOPQRSTUVW", + "claims": [], + "collection": false, + "contributors": [ + { + "contributor": "Ed Mashburn", + "contributorNameTypeId": "fbdd42a8-e47d-4694-b448-cc571d1b44c3" + } + ], + "cost": { + "currency": "USD", + "listUnitPrice": 24.99, + "quantityPhysical": 1 + }, + "description": "ABCDEFGH", + "details": { + "productIds": [ + { + "productId": "9780764354113", + "productIdType": "8261054f-be78-422d-bd51-4ed9f33c3422" + } + ], + "receivingNote": "ABCDEFGHIJKL", + "subscriptionFrom": "2018-10-09T00:00:00.000Z", + "subscriptionInterval": 824, + "subscriptionTo": "2020-10-09T00:00:00.000Z" + }, + "donor": "ABCDEFGHIJKLM", + "fundDistribution": [ + { + "code": "HIST", + "fundId": "fb7b70f1-b898-4924-a991-0e4b6312bb5f", + "encumbrance": "eb506834-6c70-4239-8d1a-6414a5b08ac3", + "distributionType": "percentage", + "value": 80.0 + } + ], + "locations": [ + { + "locationId": "fcd64ce1-6995-48f0-840e-89ffa2288371", + "quantity": 1, + "quantityElectronic": 0, + "quantityPhysical": 1 + }, + { + "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0", + "quantity": 2, + "quantityElectronic": 0, + "quantityPhysical": 2 + } + ], + "orderFormat": "Physical Resource", + "paymentStatus": "Cancelled", + "physical": { + "createInventory": "Instance, Holding, Item", + "expectedReceiptDate": "2018-10-05T00:00:00.000Z", + "materialSupplier": "73d14bc5-d131-48c6-b380-f8e62f63c8b6", + "materialType": "2fa93835-ea37-479d-b133-1d2a2279fcd8", + "receiptDue": "2018-10-10T00:00:00.000Z", + "volumes": [ + "vol. 1" + ] + }, + "poLineDescription": "ABCDEFGHIJKLMNOPQRSTUVWXY", + "poLineNumber": "1EFC97C6B7-1", + "publicationDate": "2017", + "publisher": "Schiffer Publishing", + "purchaseOrderId": "95d29d04-34b1-4fe0-a15e-1cd129143692", + "receiptDate": "2018-10-09T00:00:00.000+0000", + "receiptStatus": "Cancelled", + "reportingCodes": [], + "requester": "Leo Bulero", + "rush": true, + "selector": "ABCD", + "tags": { + "tagList": [ + "important" + ] + }, + "titleOrPackage": "Kayak Fishing in the Northern Gulf Coast", + "vendorDetail": { + "instructions": "ABCDEFG", + "noteFromVendor": "ABCDEFGHIKJKLMNOP", + "referenceNumbers": [ + { + "refNumber": "123456-78", + "refNumberType": "Vendor title number", + "vendorDetailsSource": "OrderLine" + } + ], + "vendorAccount": "8910-25" + }, + "metadata": { + "createdByUserId": "28d10cfc-d137-11e8-a8d5-f2801f1b9fd1", + "createdDate": "2014-07-06T00:00:00.000" + } +} diff --git a/src/test/resources/mockdata/lines/po_line_collection.json b/src/test/resources/mockdata/lines/po_line_collection.json index f252b3cea..607ef1717 100644 --- a/src/test/resources/mockdata/lines/po_line_collection.json +++ b/src/test/resources/mockdata/lines/po_line_collection.json @@ -924,8 +924,110 @@ "createdByUserId": "28d10cfc-d137-11e8-a8d5-f2801f1b9fd1", "createdDate": "2014-07-06T00:00:00.000" } + }, + { + "id": "1196fcd9-7607-447d-ae85-6e91883d7e4f", + "acquisitionMethod": "306489dd-0053-49ee-a068-c316444a8f55", + "alerts": [], + "cancellationRestriction": false, + "cancellationRestrictionNote": "ABCDEFGHIJKLMNOPQRSTUVW", + "claims": [], + "collection": false, + "contributors": [ + { + "contributor": "Ed Mashburn", + "contributorNameTypeId": "fbdd42a8-e47d-4694-b448-cc571d1b44c3" + } + ], + "cost": { + "currency": "USD", + "listUnitPrice": 24.99, + "quantityPhysical": 1 + }, + "description": "ABCDEFGH", + "details": { + "productIds": [ + { + "productId": "9780764354113", + "productIdType": "8261054f-be78-422d-bd51-4ed9f33c3422" + } + ], + "receivingNote": "ABCDEFGHIJKL", + "subscriptionFrom": "2018-10-09T00:00:00.000Z", + "subscriptionInterval": 824, + "subscriptionTo": "2020-10-09T00:00:00.000Z" + }, + "donor": "ABCDEFGHIJKLM", + "fundDistribution": [ + { + "code": "HIST", + "fundId": "fb7b70f1-b898-4924-a991-0e4b6312bb5f", + "encumbrance": "eb506834-6c70-4239-8d1a-6414a5b08ac3", + "distributionType": "percentage", + "value": 80.0 + } + ], + "locations": [ + { + "locationId": "fcd64ce1-6995-48f0-840e-89ffa2288371", + "quantity": 1, + "quantityElectronic": 0, + "quantityPhysical": 1 + }, + { + "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0", + "quantity": 2, + "quantityElectronic": 0, + "quantityPhysical": 2 + } + ], + "orderFormat": "Physical Resource", + "paymentStatus": "Ongoing", + "physical": { + "createInventory": "Instance, Holding, Item", + "expectedReceiptDate": "2018-10-05T00:00:00.000Z", + "materialSupplier": "73d14bc5-d131-48c6-b380-f8e62f63c8b6", + "materialType": "2fa93835-ea37-479d-b133-1d2a2279fcd8", + "receiptDue": "2018-10-10T00:00:00.000Z", + "volumes": [ + "vol. 1" + ] + }, + "poLineDescription": "ABCDEFGHIJKLMNOPQRSTUVWXY", + "poLineNumber": "1EFC97C6B7-1", + "publicationDate": "2017", + "publisher": "Schiffer Publishing", + "purchaseOrderId": "95d29d04-34b1-4fe0-a15e-1cd129143692", + "receiptDate": "2018-10-09T00:00:00.000+0000", + "receiptStatus": "Cancelled", + "reportingCodes": [], + "requester": "Leo Bulero", + "rush": true, + "selector": "ABCD", + "tags": { + "tagList": [ + "important" + ] + }, + "titleOrPackage": "Kayak Fishing in the Northern Gulf Coast", + "vendorDetail": { + "instructions": "ABCDEFG", + "noteFromVendor": "ABCDEFGHIKJKLMNOP", + "referenceNumbers": [ + { + "refNumber": "123456-78", + "refNumberType": "Vendor title number", + "vendorDetailsSource": "OrderLine" + } + ], + "vendorAccount": "8910-25" + }, + "metadata": { + "createdByUserId": "28d10cfc-d137-11e8-a8d5-f2801f1b9fd1", + "createdDate": "2014-07-06T00:00:00.000" + } } ], - "totalRecords": 9 + "totalRecords": 10 } diff --git a/src/test/resources/mockdata/pieces/pieceRecords-06a95f03-eb00-4248-9f2e-2bd05957ff05.json b/src/test/resources/mockdata/pieces/pieceRecords-06a95f03-eb00-4248-9f2e-2bd05957ff05.json new file mode 100644 index 000000000..f1909a663 --- /dev/null +++ b/src/test/resources/mockdata/pieces/pieceRecords-06a95f03-eb00-4248-9f2e-2bd05957ff05.json @@ -0,0 +1,13 @@ +{ + "pieces": [ + { + "id": "06a95f03-eb00-4248-9f2e-2bd05957ff05", + "poLineId": "1196fcd9-7607-447d-ae85-6e91883d7e4f", + "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d", + "receivingStatus": "Expected", + "receiptDate": "2018-10-09T00:00:00.000Z", + "receivedDate": "2019-03-05T08:06:58.473+0000" + } + ], + "totalRecords": 1 +} diff --git a/src/test/resources/mockdata/pieces/pieceRecordsCollection.json b/src/test/resources/mockdata/pieces/pieceRecordsCollection.json index 6722ca417..0464909ac 100644 --- a/src/test/resources/mockdata/pieces/pieceRecordsCollection.json +++ b/src/test/resources/mockdata/pieces/pieceRecordsCollection.json @@ -484,6 +484,14 @@ "receiptDate": "2018-10-09T00:00:00.000Z", "receivedDate": "2019-03-05T08:06:58.473+0000" }, + { + "id": "06a95f03-eb00-4248-9f2e-2bd05957ff05", + "poLineId": "1196fcd9-7607-447d-ae85-6e91883d7e4f", + "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d", + "receivingStatus": "Expected", + "receiptDate": "2018-10-09T00:00:00.000Z", + "receivedDate": "2019-03-05T08:06:58.473+0000" + }, { "id": "bf3c14fe-6eac-450a-90ca-391d0788a8a4", "itemId": "86481a22-633e-4b97-8061-0dc9fdaaeaca", @@ -494,5 +502,5 @@ "receivingStatus": "Received" } ], - "totalRecords": 60 + "totalRecords": 61 } diff --git a/src/test/resources/mockdata/receiving/receive-physical-cancelled.json b/src/test/resources/mockdata/receiving/receive-physical-cancelled.json new file mode 100644 index 000000000..1f280bf44 --- /dev/null +++ b/src/test/resources/mockdata/receiving/receive-physical-cancelled.json @@ -0,0 +1,20 @@ +{ + "toBeReceived": [ + { + "poLineId": "0dd8f1d2-ac2e-4155-a407-72071f6d5f4b", + "received": 1, + "receivedItems": [ + { + "barcode": 21111111122, + "callNumber": "PR 8923 W6 L36 1990 c.3", + "comment": "Very important note", + "caption": "Vol. 1", + "itemStatus": "In process", + "locationId": "fcd64ce1-6995-48f0-840e-89ffa2288371", + "pieceId": "06a95f03-eb00-4248-9f2e-2bd05957ff05" + } + ] + } + ], + "totalRecords": 1 +}