diff --git a/NEWS.md b/NEWS.md
index fc0f2b0dc1..c1b89cea92 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,7 @@
+## 24.3.1 2024-11-27
+
+* Patron notices for the trigger “Item recalled” not sent if the item is not 1st in the title request queue (CIRC-2168)
+
## 24.3.0 2024-10-31
* Support floating collections (CIRC-2136)
diff --git a/pom.xml b/pom.xml
index 8cb460a707..4572bafd77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
mod-circulation
org.folio
- 24.3.1-SNAPSHOT
+ 24.3.2-SNAPSHOT
Apache License 2.0
diff --git a/src/main/java/org/folio/circulation/resources/RequestNoticeSender.java b/src/main/java/org/folio/circulation/resources/RequestNoticeSender.java
index abbf46c61e..01e585550d 100644
--- a/src/main/java/org/folio/circulation/resources/RequestNoticeSender.java
+++ b/src/main/java/org/folio/circulation/resources/RequestNoticeSender.java
@@ -101,7 +101,8 @@ public Result sendNoticeOnRequestCreated(
Request request = records.getRequest();
recallRequestCount = records.getRequestQueue().getRequests()
.stream()
- .filter(r -> r.getRequestType() == RequestType.RECALL && r.isNotYetFilled())
+ .filter(r -> r.getRequestType() == RequestType.RECALL && r.isNotYetFilled()
+ && r.getItemId().equals(request.getItemId()))
.count();
if (request.hasItemId()) {
diff --git a/src/test/java/api/requests/RequestsAPICreationTests.java b/src/test/java/api/requests/RequestsAPICreationTests.java
index 199f01dc61..21985ad0fc 100644
--- a/src/test/java/api/requests/RequestsAPICreationTests.java
+++ b/src/test/java/api/requests/RequestsAPICreationTests.java
@@ -107,6 +107,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
+import lombok.val;
import org.apache.http.HttpStatus;
import org.awaitility.Awaitility;
import org.folio.circulation.domain.ItemStatus;
@@ -3763,6 +3764,84 @@ void itemCheckOutRecallCancelAgainRecallRequestCreationShouldProduceNotice() {
validateNoticeLogContextItem(noticeLogContextItemLogs.get(1), item);
}
+ @Test
+ void shouldTriggerNoticesForTitleLevelRecall() {
+ // Enable the Title Level Request feature
+ configurationsFixture.enableTlrFeature();
+
+ // Configure recall notice for the loan owner (borrower)
+ JsonObject recallToLoaneeConfiguration = new NoticeConfigurationBuilder()
+ .withTemplateId(UUID.randomUUID())
+ .withEventType(NoticeEventType.ITEM_RECALLED.getRepresentation())
+ .create();
+
+ // Configure recall request notice for the requester
+ JsonObject recallRequestToRequesterConfiguration = new NoticeConfigurationBuilder()
+ .withTemplateId(UUID.randomUUID())
+ .withEventType(NoticeEventType.RECALL_REQUEST.getRepresentation())
+ .create();
+
+ // Create a notice policy with the above configurations
+ NoticePolicyBuilder noticePolicy = new NoticePolicyBuilder()
+ .withName("Policy with recall notice")
+ .withLoanNotices(List.of(recallToLoaneeConfiguration, recallRequestToRequesterConfiguration));
+
+ useFallbackPolicies(
+ loanPoliciesFixture.canCirculateRolling().getId(),
+ requestPoliciesFixture.allowAllRequestPolicy().getId(),
+ noticePoliciesFixture.create(noticePolicy).getId(),
+ overdueFinePoliciesFixture.facultyStandard().getId(),
+ lostItemFeePoliciesFixture.facultyStandard().getId());
+
+ // Create 3 items belonging to the same instance.
+ // The notice issue occurs only when the request queue has more than 1 item.
+ // So we need to create items under same instance to test that issue
+ val items = itemsFixture.createMultipleItemForTheSameInstance(3,
+ List.of(itemsFixture.addCallNumberStringComponents("1"),
+ itemsFixture.addCallNumberStringComponents("2"), itemsFixture.addCallNumberStringComponents("3")));
+
+ // Create borrowers who will loan the items
+ IndividualResource borrower1 = usersFixture.steve();
+ IndividualResource borrower2 = usersFixture.jessica();
+ IndividualResource borrower3 = usersFixture.james();
+
+ // Create requesters who will place title-level recall requests
+ IndividualResource requester1 = usersFixture.charlotte();
+ IndividualResource requester2 = usersFixture.rebecca();
+ IndividualResource requester3 = usersFixture.bobby();
+ IndividualResource requester4 = usersFixture.henry();
+
+ // Check out items for the borrowers
+ checkOutFixture.checkOutByBarcode(items.get(0), borrower1);
+ checkOutFixture.checkOutByBarcode(items.get(1), borrower2);
+ checkOutFixture.checkOutByBarcode(items.get(2), borrower3);
+
+ // Place title-level recall requests on the same instance
+ requestsFixture.placeTitleLevelRecallRequest(items.get(0).getInstanceId(), requester1);
+ requestsFixture.placeTitleLevelRecallRequest(items.get(0).getInstanceId(), requester2);
+ requestsFixture.placeTitleLevelRecallRequest(items.get(0).getInstanceId(), requester3);
+ requestsFixture.placeTitleLevelRecallRequest(items.get(0).getInstanceId(), requester4);
+
+
+ // Verify the notices are triggered as expected
+ // There should be 7 notices triggered: 4 recall request notices and 3 item recall notices
+ Awaitility.waitAtMost(1, TimeUnit.SECONDS)
+ .until(() -> getPublishedEventsAsList(byLogEventType(NOTICE)), hasSize(7));
+
+ // Verify the number of notices sent and events published.
+ // Requester will receive the recall request notice and borrower will receive the item recalled notice
+ verifyNumberOfSentNotices(7);
+ verifyNumberOfNoticeEventsForUser(requester1.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(requester2.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(requester3.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(requester4.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(borrower1.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(borrower2.getId(), 1);
+ verifyNumberOfNoticeEventsForUser(borrower3.getId(), 1);
+ verifyNumberOfPublishedEvents(NOTICE, 7);
+ verifyNumberOfPublishedEvents(NOTICE_ERROR, 0);
+ }
+
private void verifyNumberOfNoticeEventsForUser(UUID userId, int expectedNoticeEventsCount) {
int noticeEventsCount = (int) getPublishedEventsAsList(byLogEventType(NOTICE))
.stream()