Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/few-dryers-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/model-typings": patch
"@rocket.chat/models": patch
---

Allows agents to set a default agent when the chat being transferred ends up in the queue
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi
isWaitingQueueEnabled,
});
await saveTransferHistory(room, transferData);
return RoutingManager.unassignAgent(inquiry, departmentId, true);
return RoutingManager.unassignAgent(inquiry, departmentId, true, agent);
}

// Fake the department to forward the inquiry - Case the forward process does not success
Expand Down
21 changes: 17 additions & 4 deletions apps/meteor/app/livechat/server/lib/RoutingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ type Routing = {
options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any } },
room?: IOmnichannelRoom,
): Promise<(IOmnichannelRoom & { chatQueued?: boolean }) | null | void>;
unassignAgent(inquiry: ILivechatInquiryRecord, departmentId?: string, shouldQueue?: boolean): Promise<boolean>;
unassignAgent(
inquiry: ILivechatInquiryRecord,
departmentId?: string,
shouldQueue?: boolean,
agent?: SelectedAgent | null,
Comment thread
KevLehman marked this conversation as resolved.
): Promise<boolean>;
takeInquiry(
inquiry: Omit<
ILivechatInquiryRecord,
Expand Down Expand Up @@ -157,11 +162,19 @@ export const RoutingManager: Routing = {
return { inquiry, user };
},

async unassignAgent(inquiry, departmentId, shouldQueue = false) {
async unassignAgent(inquiry, departmentId, shouldQueue = false, defaultAgent?: SelectedAgent | null) {
const { rid, department } = inquiry;
const room = await LivechatRooms.findOneById(rid);

logger.debug(`Removing assignations of inquiry ${inquiry._id}`);
logger.debug({
msg: 'Removing assignations of inquiry',
inquiryId: inquiry._id,
departmentId,
room: { _id: room?._id, open: room?.open, servedBy: room?.servedBy },
shouldQueue,
...(defaultAgent && { defaultAgent }),
Comment thread
KevLehman marked this conversation as resolved.
Outdated
});

if (!room?.open) {
logger.debug(`Cannot unassign agent from inquiry ${inquiry._id}: Room already closed`);
return false;
Expand All @@ -187,7 +200,7 @@ export const RoutingManager: Routing = {
}

if (shouldQueue) {
const queuedInquiry = await LivechatInquiry.queueInquiry(inquiry._id, room.lastMessage);
const queuedInquiry = await LivechatInquiry.queueInquiry(inquiry._id, room.lastMessage, defaultAgent);
if (queuedInquiry) {
inquiry = queuedInquiry;
void notifyOnLivechatInquiryChanged(inquiry, 'updated', {
Expand Down
38 changes: 38 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,44 @@ describe('LIVECHAT - rooms', () => {
]);
});

(IS_EE ? it : it.skip)(
'when manager forwards to department & passes an agent on the request while waiting queue is active, the inquiry should be set to the queue with default agent',
async () => {
const { department: initialDepartment } = await createDepartmentWithAnOnlineAgent();
const { department: forwardToOfflineDepartment, agent: onlineAgent } = await createDepartmentWithAnOnlineAgent();
await updateSetting('Livechat_waiting_queue', true);

const newVisitor = await createVisitor(initialDepartment._id);
const newRoom = await createLivechatRoom(newVisitor.token);

const manager = await createUser();
const managerCredentials = await login(manager.username, password);
await createManager(manager.username);

await request.post(api('livechat/room.forward')).set(managerCredentials).send({
roomId: newRoom._id,
departmentId: forwardToOfflineDepartment._id,
userId: onlineAgent.user._id,
clientAction: true,
comment: 'test comment',
});

const inquiry = await fetchInquiry(newRoom._id);

expect(inquiry).to.have.property('status', 'queued');
expect(inquiry)
.to.have.property('defaultAgent')
.to.deep.equal({ agentId: onlineAgent.user._id, username: onlineAgent.user.username });

await Promise.all([
updateSetting('Livechat_waiting_queue', false),
deleteDepartment(initialDepartment._id),
deleteDepartment(forwardToOfflineDepartment._id),
closeOmnichannelRoom(newRoom._id),
]);
},
);

(IS_EE ? it : it.skip)(
'when manager forward to offline (agent away, accept when agent idle off) department the inquiry should be set to the queue',
async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/model-typings/src/models/ILivechatInquiryModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IMessage, ILivechatInquiryRecord, LivechatInquiryStatus } from '@rocket.chat/core-typings';
import type { IMessage, ILivechatInquiryRecord, LivechatInquiryStatus, SelectedAgent } from '@rocket.chat/core-typings';
import type { FindOptions, Document, UpdateResult, DeleteResult, FindCursor, DeleteOptions, AggregateOptions } from 'mongodb';

import type { IBaseModel } from './IBaseModel';
Expand Down Expand Up @@ -33,7 +33,7 @@ export interface ILivechatInquiryModel extends IBaseModel<ILivechatInquiryRecord
getQueuedInquiries(options?: FindOptions<ILivechatInquiryRecord>): FindCursor<ILivechatInquiryRecord>;
takeInquiry(inquiryId: string): Promise<void>;
openInquiry(inquiryId: string): Promise<UpdateResult>;
queueInquiry(inquiryId: string, lastMessage?: IMessage): Promise<ILivechatInquiryRecord | null>;
queueInquiry(inquiryId: string, lastMessage?: IMessage, defaultAgent?: SelectedAgent | null): Promise<ILivechatInquiryRecord | null>;
queueInquiryAndRemoveDefaultAgent(inquiryId: string): Promise<UpdateResult>;
readyInquiry(inquiryId: string): Promise<UpdateResult>;
changeDepartmentIdByRoomId(rid: string, department: string): Promise<UpdateResult>;
Expand Down
15 changes: 13 additions & 2 deletions packages/models/src/models/LivechatInquiry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { ILivechatInquiryRecord, IMessage, RocketChatRecordDeleted, ILivechatPriority } from '@rocket.chat/core-typings';
import type {
ILivechatInquiryRecord,
IMessage,
RocketChatRecordDeleted,
ILivechatPriority,
SelectedAgent,
} from '@rocket.chat/core-typings';
import { LivechatInquiryStatus } from '@rocket.chat/core-typings';
import type { ILivechatInquiryModel } from '@rocket.chat/model-typings';
import type {
Expand Down Expand Up @@ -331,7 +337,11 @@ export class LivechatInquiryRaw extends BaseRaw<ILivechatInquiryRecord> implemen
);
}

async queueInquiry(inquiryId: string, lastMessage?: IMessage): Promise<ILivechatInquiryRecord | null> {
async queueInquiry(
inquiryId: string,
lastMessage?: IMessage,
defaultAgent?: SelectedAgent | null,
): Promise<ILivechatInquiryRecord | null> {
return this.findOneAndUpdate(
{
_id: inquiryId,
Expand All @@ -341,6 +351,7 @@ export class LivechatInquiryRaw extends BaseRaw<ILivechatInquiryRecord> implemen
status: LivechatInquiryStatus.QUEUED,
queuedAt: new Date(),
...(lastMessage && { lastMessage }),
...(defaultAgent && { defaultAgent }),
},
$unset: { takenAt: 1 },
},
Expand Down
Loading