From ea3f4fedb09b5d65252cbe560caa3bdc9876ecee Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Tue, 8 Apr 2025 09:21:34 +0200 Subject: [PATCH] fix: weight filter should never return length=0 (#20588) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix error * add test * return host weight instead * adjust test * remove .only from test --------- Co-authored-by: CarinaWolli Co-authored-by: Omar López --- packages/lib/server/getLuckyUser.test.ts | 130 +++++++++++++++++++++++ packages/lib/server/getLuckyUser.ts | 13 ++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/packages/lib/server/getLuckyUser.test.ts b/packages/lib/server/getLuckyUser.test.ts index e28351fed7..23245eb028 100644 --- a/packages/lib/server/getLuckyUser.test.ts +++ b/packages/lib/server/getLuckyUser.test.ts @@ -1054,6 +1054,136 @@ describe("attribute weights and virtual queues", () => { }); }); + it("prepareQueuesAndAttributesData returns host weights as fallback when no members are assigned to the attribute", async () => { + const attributeOptionIdFirst = uuid(); + const attributeOptionIdSecond = uuid(); + const attributeId = uuid(); + const routeId = uuid(); + const fieldId = uuid(); + + const routingFormResponse = { + response: { + [fieldId]: { label: "headquarters", value: attributeOptionIdSecond }, + }, + form: { + routes: [ + { + id: uuid(), + action: { type: "eventTypeRedirectUrl", value: "team/team1/team1-event-1", eventTypeId: 29 }, + queryValue: { id: "a98ab8a9-4567-489a-bcde-f1932649bb8b", type: "group" }, + attributesQueryValue: { + id: "b8ab8ba9-0123-4456-b89a-b1932649bb8b", + type: "group", + children1: { + "a8999bb9-89ab-4cde-b012-31932649cc93": { + type: "rule", + properties: { + field: uuid(), //another attribute + value: [[`{field:${fieldId}}`]], + operator: "multiselect_some_in", + valueSrc: ["value"], + valueType: ["multiselect"], + valueError: [null], + }, + }, + }, + }, + attributeRoutingConfig: {}, + }, + { + //chosen route + id: routeId, + attributeIdForWeights: attributeId, + action: { type: "eventTypeRedirectUrl", value: "team/team1/team1-event-1", eventTypeId: 29 }, + queryValue: { id: "a98ab8a9-4567-489a-bcde-f1932649bb8b", type: "group" }, + attributesQueryValue: { + id: "b8ab8ba9-0123-4456-b89a-b1932649bb8b", + type: "group", + children1: { + "a8999bb9-89ab-4cde-b012-31932649cc93": { + type: "rule", + properties: { + field: attributeId, + value: [[`{field:${fieldId}}`]], + operator: "multiselect_some_in", + valueSrc: ["value"], + valueType: ["multiselect"], + valueError: [null], + }, + }, + }, + }, + attributeRoutingConfig: {}, + }, + ], + fields: [ + { + id: fieldId, + type: "select", + label: "headquarters", + options: [ + { id: attributeOptionIdFirst, label: "USA" }, + { id: attributeOptionIdSecond, label: "Germany" }, + ], + required: true, + }, + ], + }, + chosenRouteId: routeId, + }; + + prismaMock.attribute.findUnique.mockResolvedValue({ + name: "Headquaters", + id: attributeId, + type: AttributeType.SINGLE_SELECT, + slug: "headquarters", + options: [ + { + id: "12345", + value: "Germany", + slug: "Germany", + assignedUsers: [], + }, + ], + }); + + const queuesAndAttributesData = await prepareQueuesAndAttributesData({ + eventType: { + id: 1, + isRRWeightsEnabled: true, + team: { parentId: 1, rrResetInterval: RRResetInterval.DAY }, + }, + routingFormResponse, + allRRHosts: [ + { + user: { + id: 1, + email: "test1@example.com", + credentials: [], + selectedCalendars: [], + }, + createdAt: new Date(), + weight: 10, + }, + { + user: { + id: 2, + email: "test2@example.com", + credentials: [], + selectedCalendars: [], + }, + createdAt: new Date(), + weight: 150, + }, + ], + }); + + expect(queuesAndAttributesData.attributeWeights).toEqual([ + { userId: 1, weight: 10 }, + { userId: 2, weight: 150 }, + ]); + }); + it("uses attribute weights and counts only bookings within virtual queue", async () => { const users: GetLuckyUserAvailableUsersType = [ buildUser({ diff --git a/packages/lib/server/getLuckyUser.ts b/packages/lib/server/getLuckyUser.ts index 1b39e9e820..e078169be3 100644 --- a/packages/lib/server/getLuckyUser.ts +++ b/packages/lib/server/getLuckyUser.ts @@ -295,7 +295,7 @@ function filterUsersBasedOnWeights< // Calculate the total calibration and weight of all round-robin hosts let totalWeight: number; - if (attributeWeights) { + if (attributeWeights && attributeWeights.length > 0) { totalWeight = attributeWeights.reduce((totalWeight, userWeight) => { totalWeight += userWeight.weight ?? 100; return totalWeight; @@ -1056,16 +1056,15 @@ function getAverageAttributeWeights< ); allRRHosts.forEach((rrHost) => { + //assignedUser can be undefined if fallback route is hit or in the case of crm ownership const assignedUser = attributeOptionWithUsers?.assignedUsers.find( (assignedUser) => rrHost.user.id === assignedUser.member.userId ); - if (assignedUser) { - if (allRRHostsWeights.has(rrHost.user.id)) { - allRRHostsWeights.get(rrHost.user.id)?.push(assignedUser.weight ?? 100); - } else { - allRRHostsWeights.set(rrHost.user.id, [assignedUser.weight ?? 100]); - } + if (allRRHostsWeights.has(rrHost.user.id)) { + allRRHostsWeights.get(rrHost.user.id)?.push(assignedUser?.weight ?? rrHost.weight ?? 100); + } else { + allRRHostsWeights.set(rrHost.user.id, [assignedUser?.weight ?? rrHost.weight ?? 100]); } }); });