diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts
index 3a76c7ce06fa..cc32d2909f21 100644
--- a/apps/meteor/app/livechat/imports/server/rest/departments.ts
+++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts
@@ -12,6 +12,7 @@ import {
findDepartmentsBetweenIds,
findDepartmentAgents,
} from '../../../server/api/lib/departments';
+import { DepartmentHelper } from '../../../server/lib/Departments';
API.v1.addRoute(
'livechat/department',
@@ -133,15 +134,14 @@ API.v1.addRoute(
return API.v1.failure();
},
- delete() {
+ async delete() {
check(this.urlParams, {
_id: String,
});
- if (Livechat.removeDepartment(this.urlParams._id)) {
- return API.v1.success();
- }
- return API.v1.failure();
+ await DepartmentHelper.removeDepartment(this.urlParams._id);
+
+ return API.v1.success();
},
},
);
diff --git a/apps/meteor/app/livechat/server/lib/Departments.ts b/apps/meteor/app/livechat/server/lib/Departments.ts
new file mode 100644
index 000000000000..ec9e66093470
--- /dev/null
+++ b/apps/meteor/app/livechat/server/lib/Departments.ts
@@ -0,0 +1,56 @@
+import { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms } from '@rocket.chat/models';
+
+import { callbacks } from '../../../../lib/callbacks';
+import { Logger } from '../../../logger/server';
+
+class DepartmentHelperClass {
+ logger = new Logger('Omnichannel:DepartmentHelper');
+
+ async removeDepartment(departmentId: string) {
+ this.logger.debug(`Removing department: ${departmentId}`);
+
+ const department = await LivechatDepartment.findOneById(departmentId);
+ if (!department) {
+ this.logger.debug(`Department not found: ${departmentId}`);
+ throw new Error('error-department-not-found');
+ }
+
+ const { _id } = department;
+
+ const ret = await LivechatDepartment.removeById(_id);
+ if (ret.acknowledged !== true) {
+ this.logger.error(`Department record not removed: ${_id}. Result from db: ${ret}`);
+ throw new Error('error-failed-to-delete-department');
+ }
+ this.logger.debug(`Department record removed: ${_id}`);
+
+ const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId(department._id)
+ .cursor.map((agent) => agent.agentId)
+ .toArray();
+
+ this.logger.debug(
+ `Performing post-department-removal actions: ${_id}. Removing department agents, unsetting fallback department and removing department from rooms`,
+ );
+
+ const promiseResponses = await Promise.allSettled([
+ LivechatDepartmentAgents.removeByDepartmentId(_id),
+ LivechatDepartment.unsetFallbackDepartmentByDepartmentId(_id),
+ LivechatRooms.bulkRemoveDepartmentAndUnitsFromRooms(_id),
+ ]);
+ promiseResponses.forEach((response, index) => {
+ if (response.status === 'rejected') {
+ this.logger.error(`Error while performing post-department-removal actions: ${_id}. Action No: ${index}. Error:`, response.reason);
+ }
+ });
+
+ this.logger.debug(`Post-department-removal actions completed: ${_id}. Notifying callbacks with department and agentsIds`);
+
+ Meteor.defer(() => {
+ callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds });
+ });
+
+ return ret;
+ }
+}
+
+export const DepartmentHelper = new DepartmentHelperClass();
diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js
index 47ae2de29551..6d32b0c2e834 100644
--- a/apps/meteor/app/livechat/server/lib/Livechat.js
+++ b/apps/meteor/app/livechat/server/lib/Livechat.js
@@ -1129,30 +1129,6 @@ export const Livechat = {
return true;
},
- removeDepartment(_id) {
- check(_id, String);
-
- const department = LivechatDepartment.findOneById(_id, { fields: { _id: 1 } });
-
- if (!department) {
- throw new Meteor.Error('department-not-found', 'Department not found', {
- method: 'livechat:removeDepartment',
- });
- }
- const ret = LivechatDepartment.removeById(_id);
- const agentsIds = LivechatDepartmentAgents.findByDepartmentId(_id)
- .fetch()
- .map((agent) => agent.agentId);
- LivechatDepartmentAgents.removeByDepartmentId(_id);
- LivechatDepartment.unsetFallbackDepartmentByDepartmentId(_id);
- if (ret) {
- Meteor.defer(() => {
- callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds });
- });
- }
- return ret;
- },
-
showConnecting() {
const { showConnecting } = RoutingManager.getConfig();
return showConnecting;
diff --git a/apps/meteor/app/livechat/server/methods/removeDepartment.js b/apps/meteor/app/livechat/server/methods/removeDepartment.js
index 226fb1153376..c4d64ee25c12 100644
--- a/apps/meteor/app/livechat/server/methods/removeDepartment.js
+++ b/apps/meteor/app/livechat/server/methods/removeDepartment.js
@@ -1,19 +1,22 @@
import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
import { hasPermission } from '../../../authorization';
-import { Livechat } from '../lib/Livechat';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
+import { DepartmentHelper } from '../lib/Departments';
Meteor.methods({
'livechat:removeDepartment'(_id) {
methodDeprecationLogger.warn('livechat:removeDepartment will be deprecated in future versions of Rocket.Chat');
+ check(_id, String);
+
if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'manage-livechat-departments')) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'livechat:removeDepartment',
});
}
- return Livechat.removeDepartment(_id);
+ return DepartmentHelper.removeDepartment(_id);
},
});
diff --git a/apps/meteor/app/models/server/models/LivechatDepartment.js b/apps/meteor/app/models/server/models/LivechatDepartment.js
index 4f8a22fa04b0..4bed4d175dfd 100644
--- a/apps/meteor/app/models/server/models/LivechatDepartment.js
+++ b/apps/meteor/app/models/server/models/LivechatDepartment.js
@@ -152,18 +152,6 @@ export class LivechatDepartment extends Base {
return this.find(query, options);
}
-
- unsetFallbackDepartmentByDepartmentId(_id) {
- return this.update(
- { fallbackForwardDepartment: _id },
- {
- $unset: {
- fallbackForwardDepartment: 1,
- },
- },
- { multi: true },
- );
- }
}
export default new LivechatDepartment();
diff --git a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js b/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js
index d3e9111b33f5..5ec1494a70fd 100644
--- a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js
+++ b/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js
@@ -52,10 +52,6 @@ export class LivechatDepartmentAgents extends Base {
this.remove({ departmentId, agentId });
}
- removeByDepartmentId(departmentId) {
- this.remove({ departmentId });
- }
-
getNextAgentForDepartment(departmentId, ignoreAgentId, extraQuery) {
const agents = this.findByDepartmentId(departmentId).fetch();
diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.js
deleted file mode 100644
index 7b1209364de8..000000000000
--- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useTranslation } from '@rocket.chat/ui-contexts';
-import React from 'react';
-
-import { AsyncStatePhase } from '../../../../../hooks/useAsyncState';
-import { useEndpointData } from '../../../../../hooks/useEndpointData';
-import Field from '../../../components/Field';
-import Info from '../../../components/Info';
-import Label from '../../../components/Label';
-import { FormSkeleton } from '../../Skeleton';
-
-const DepartmentField = ({ departmentId }) => {
- const t = useTranslation();
- const { value: data, phase: state } = useEndpointData('/v1/livechat/department/:_id', { keys: { _id: departmentId } });
- if (state === AsyncStatePhase.LOADING) {
- return ;
- }
- const { department: { name } = {} } = data || { department: {} };
- return (
-
-
- {name || t('Department_not_found')}
-
- );
-};
-
-export default DepartmentField;
diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.tsx b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.tsx
new file mode 100644
index 000000000000..a12bba236965
--- /dev/null
+++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.tsx
@@ -0,0 +1,28 @@
+import { Box, Skeleton } from '@rocket.chat/fuselage';
+import { useTranslation } from '@rocket.chat/ui-contexts';
+import React from 'react';
+
+import Field from '../../../components/Field';
+import Info from '../../../components/Info';
+import Label from '../../../components/Label';
+import { useDepartmentInfo } from '../../hooks/useDepartmentInfo';
+
+type DepartmentFieldProps = {
+ departmentId: string;
+};
+
+const DepartmentField = ({ departmentId }: DepartmentFieldProps) => {
+ const t = useTranslation();
+ const { data, isLoading, isError } = useDepartmentInfo(departmentId);
+
+ return (
+
+
+ {isLoading && }
+ {isError && {t('Something_went_wrong')}}
+ {!isLoading && !isError && {data?.department?.name || t('Department_not_found')}}
+
+ );
+};
+
+export default DepartmentField;
diff --git a/apps/meteor/client/views/omnichannel/directory/hooks/useDepartmentInfo.ts b/apps/meteor/client/views/omnichannel/directory/hooks/useDepartmentInfo.ts
new file mode 100644
index 000000000000..076d794567b3
--- /dev/null
+++ b/apps/meteor/client/views/omnichannel/directory/hooks/useDepartmentInfo.ts
@@ -0,0 +1,10 @@
+import type { OperationResult } from '@rocket.chat/rest-typings';
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import type { UseQueryResult } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
+
+export const useDepartmentInfo = (departmentId: string): UseQueryResult> => {
+ const deptInfo = useEndpoint('GET', `/v1/livechat/department/:_id`, { _id: departmentId });
+
+ return useQuery(['livechat/department', departmentId], () => deptInfo({}));
+};
diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.js b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.js
deleted file mode 100644
index 8a17be5f27fd..000000000000
--- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { callbacks } from '../../../../../lib/callbacks';
-import { LivechatDepartment } from '../../../../../app/models/server';
-
-callbacks.add(
- 'livechat.afterRemoveDepartment',
- (options = {}) => {
- const { department } = options;
- if (!department) {
- return options;
- }
- LivechatDepartment.removeDepartmentFromForwardListById(department._id);
- return options;
- },
- callbacks.priority.HIGH,
- 'livechat-after-remove-department',
-);
diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.ts
new file mode 100644
index 000000000000..6f4adbf557d6
--- /dev/null
+++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterRemoveDepartment.ts
@@ -0,0 +1,30 @@
+import type { ILivechatAgent, ILivechatDepartmentRecord } from '@rocket.chat/core-typings';
+import { LivechatDepartment } from '@rocket.chat/models';
+
+import { callbacks } from '../../../../../lib/callbacks';
+import { cbLogger } from '../lib/logger';
+
+const afterRemoveDepartment = async (options: { department: ILivechatDepartmentRecord; agentsId: ILivechatAgent['_id'][] }) => {
+ cbLogger.debug(`Performing post-department-removal actions in EE: ${options?.department?._id}. Removing department from forward list`);
+ if (!options || !options.department) {
+ cbLogger.warn('No department found in options', options);
+ return options;
+ }
+
+ const { department } = options;
+
+ cbLogger.debug(`Removing department from forward list: ${department._id}`);
+ await LivechatDepartment.removeDepartmentFromForwardListById(department._id);
+ cbLogger.debug(`Removed department from forward list: ${department._id}`);
+
+ cbLogger.debug(`Post-department-removal actions completed in EE: ${department._id}`);
+
+ return options;
+};
+
+callbacks.add(
+ 'livechat.afterRemoveDepartment',
+ (options) => Promise.await(afterRemoveDepartment(options)),
+ callbacks.priority.HIGH,
+ 'livechat-after-remove-department',
+);
diff --git a/apps/meteor/ee/app/models/server/models/LivechatDepartment.js b/apps/meteor/ee/app/models/server/models/LivechatDepartment.js
index cfa218c9db51..163ae5e72d1d 100644
--- a/apps/meteor/ee/app/models/server/models/LivechatDepartment.js
+++ b/apps/meteor/ee/app/models/server/models/LivechatDepartment.js
@@ -55,8 +55,4 @@ overwriteClassOnLicense('livechat-enterprise', LivechatDepartment, {
},
});
-LivechatDepartment.prototype.removeDepartmentFromForwardListById = function (_id) {
- return this.update({ departmentsAllowedToForward: _id }, { $pull: { departmentsAllowedToForward: _id } }, { multi: true });
-};
-
export default LivechatDepartment;
diff --git a/apps/meteor/ee/server/models/LivechatDepartment.ts b/apps/meteor/ee/server/models/LivechatDepartment.ts
new file mode 100644
index 000000000000..620983c7711d
--- /dev/null
+++ b/apps/meteor/ee/server/models/LivechatDepartment.ts
@@ -0,0 +1,6 @@
+import { registerModel } from '@rocket.chat/models';
+
+import { db } from '../../../server/database/utils';
+import { LivechatDepartmentEE } from './raw/LivechatDepartment';
+
+registerModel('ILivechatDepartmentModel', new LivechatDepartmentEE(db));
diff --git a/apps/meteor/ee/server/models/raw/LivechatDepartment.ts b/apps/meteor/ee/server/models/raw/LivechatDepartment.ts
new file mode 100644
index 000000000000..589f47216ccd
--- /dev/null
+++ b/apps/meteor/ee/server/models/raw/LivechatDepartment.ts
@@ -0,0 +1,15 @@
+import type { ILivechatDepartmentModel } from '@rocket.chat/model-typings';
+
+import { LivechatDepartmentRaw } from '../../../../server/models/raw/LivechatDepartment';
+
+declare module '@rocket.chat/model-typings' {
+ export interface ILivechatDepartmentModel {
+ removeDepartmentFromForwardListById(departmentId: string): Promise;
+ }
+}
+
+export class LivechatDepartmentEE extends LivechatDepartmentRaw implements ILivechatDepartmentModel {
+ async removeDepartmentFromForwardListById(departmentId: string): Promise {
+ await this.updateMany({ departmentsAllowedToForward: departmentId }, { $pull: { departmentsAllowedToForward: departmentId } });
+ }
+}
diff --git a/apps/meteor/ee/server/models/startup.ts b/apps/meteor/ee/server/models/startup.ts
index 9645c9cd0501..7b803b4c9521 100644
--- a/apps/meteor/ee/server/models/startup.ts
+++ b/apps/meteor/ee/server/models/startup.ts
@@ -7,4 +7,5 @@ onLicense('livechat-enterprise', () => {
import('./LivechatUnit');
import('./LivechatUnitMonitors');
import('./LivechatRooms');
+ import('./LivechatDepartment');
});
diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts
index da6c8b1d1cc5..7eed03bcbf29 100644
--- a/apps/meteor/lib/callbacks.ts
+++ b/apps/meteor/lib/callbacks.ts
@@ -107,7 +107,7 @@ type ChainedCallbackSignatures = {
oldDepartmentId: ILivechatDepartmentRecord['_id'];
};
'livechat.afterInquiryQueued': (inquiry: ILivechatInquiryRecord) => ILivechatInquiryRecord;
- 'livechat.afterRemoveDepartment': (params: { departmentId: ILivechatDepartmentRecord['_id']; agentsId: ILivechatAgent['_id'][] }) => {
+ 'livechat.afterRemoveDepartment': (params: { department: ILivechatDepartmentRecord; agentsId: ILivechatAgent['_id'][] }) => {
departmentId: ILivechatDepartmentRecord['_id'];
agentsId: ILivechatAgent['_id'][];
};
diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
index 4fb24dac3515..8ecd2fe353d7 100644
--- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -1880,6 +1880,7 @@
"error-email-domain-blacklisted": "The email domain is blacklisted",
"error-email-send-failed": "Error trying to send email: __message__",
"error-essential-app-disabled": "Error: a Rocket.Chat App that is essential for this is disabled. Please contact your administrator",
+ "error-failed-to-delete-department": "Failed to delete department",
"error-field-unavailable": "__field__ is already in use :(",
"error-file-too-large": "File is too large",
"error-forwarding-chat": "Something went wrong while forwarding the chat, Please try again later.",
@@ -5550,4 +5551,4 @@
"Theme_dark": "Dark",
"Join_your_team": "Join your team",
"Create_an_account": "Create an account"
-}
\ No newline at end of file
+}
diff --git a/apps/meteor/server/models/raw/LivechatDepartment.ts b/apps/meteor/server/models/raw/LivechatDepartment.ts
index ee03b2ab31a8..6338e98b1222 100644
--- a/apps/meteor/server/models/raw/LivechatDepartment.ts
+++ b/apps/meteor/server/models/raw/LivechatDepartment.ts
@@ -110,4 +110,12 @@ export class LivechatDepartmentRaw extends BaseRaw im
return this.updateMany(query, update);
}
+
+ unsetFallbackDepartmentByDepartmentId(departmentId: string): Promise {
+ return this.updateMany({ fallbackDepartment: departmentId }, { $unset: { fallbackDepartment: 1 } });
+ }
+
+ removeDepartmentFromForwardListById(_departmentId: string): Promise {
+ throw new Error('Method not implemented in Community Edition.');
+ }
}
diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts
index a55dea5601e1..5e9a278b2db7 100644
--- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts
+++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts
@@ -1,6 +1,6 @@
import type { ILivechatDepartmentAgents, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { FindPaginated, ILivechatDepartmentAgentsModel } from '@rocket.chat/model-typings';
-import type { Collection, FindCursor, Db, Filter, FindOptions } from 'mongodb';
+import type { Collection, FindCursor, Db, Filter, FindOptions, DeleteResult } from 'mongodb';
import { BaseRaw } from './BaseRaw';
@@ -105,4 +105,8 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw {
+ return this.deleteOne({ departmentId });
+ }
}
diff --git a/apps/meteor/server/models/raw/LivechatRooms.js b/apps/meteor/server/models/raw/LivechatRooms.js
index 5e0f9f2af9f5..542f05ddbb02 100644
--- a/apps/meteor/server/models/raw/LivechatRooms.js
+++ b/apps/meteor/server/models/raw/LivechatRooms.js
@@ -1288,4 +1288,8 @@ export class LivechatRoomsRaw extends BaseRaw {
},
]);
}
+
+ bulkRemoveDepartmentAndUnitsFromRooms(departmentId) {
+ return this.updateMany({ departmentId }, { $unset: { departmentId: 1, departmentAncestors: 1 } });
+ }
}
diff --git a/apps/meteor/tests/data/livechat/units.ts b/apps/meteor/tests/data/livechat/units.ts
index 07f11cfc3ca2..3b5a94c0132b 100644
--- a/apps/meteor/tests/data/livechat/units.ts
+++ b/apps/meteor/tests/data/livechat/units.ts
@@ -25,7 +25,7 @@ export const createMonitor = async (username: string): Promise<{ _id: string; us
});
};
-export const createUnit = async (monitorId: string, username: string, departmentId: string): Promise => {
+export const createUnit = async (monitorId: string, username: string, departmentIds: string[]): Promise => {
return new Promise((resolve, reject) => {
request
.post(methodCall(`livechat:saveUnit`))
@@ -33,7 +33,7 @@ export const createUnit = async (monitorId: string, username: string, department
.send({
message: JSON.stringify({
method: 'livechat:saveUnit',
- params: [null, { name: faker.name.firstName(), visibility: faker.helpers.arrayElement(['public', 'private']) }, [{ monitorId, username }], [{ departmentId }]],
+ params: [null, { name: faker.name.firstName(), visibility: faker.helpers.arrayElement(['public', 'private']) }, [{ monitorId, username }], departmentIds.map((departmentId) => ({ departmentId }))],
id: '101',
msg: 'method',
}),
diff --git a/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts b/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts
index df182eee81c7..88e5e6167a5d 100644
--- a/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts
+++ b/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts
@@ -5,7 +5,18 @@ import type { Response } from 'supertest';
import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
-import { makeAgentAvailable, createAgent, createDepartment } from '../../../data/livechat/rooms';
+import {
+ makeAgentAvailable,
+ createAgent,
+ createDepartment,
+ createVisitor,
+ createLivechatRoom,
+ getLivechatRoomInfo,
+} from '../../../data/livechat/rooms';
+import { createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department';
+import { IS_EE } from '../../../e2e/config/constants';
+import { createUser } from '../../../data/users.helper';
+import { createMonitor, createUnit } from '../../../data/livechat/units';
describe('LIVECHAT - Departments', function () {
before((done) => getCredentials(done));
@@ -153,6 +164,129 @@ describe('LIVECHAT - Departments', function () {
});
});
+ describe('DELETE livechat/department/:_id', () => {
+ it('should return unauthorized error when the user does not have the necessary permission', async () => {
+ await updatePermission('manage-livechat-departments', []);
+ await updatePermission('remove-livechat-department', []);
+
+ await request
+ .delete(api('livechat/department/testetetetstetete'))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(403);
+ });
+
+ it('should return an error when the department does not exist', async () => {
+ await updatePermission('manage-livechat-departments', ['admin']);
+
+ const resp: Response = await request
+ .delete(api('livechat/department/testesteteste'))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(400);
+
+ expect(resp.body).to.have.property('success', false);
+ expect(resp.body).to.have.property('error', 'error-department-not-found');
+ });
+
+ it('it should remove the department', async () => {
+ const department = await createDepartment();
+
+ const resp: Response = await request
+ .delete(api(`livechat/department/${department._id}`))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(200);
+
+ expect(resp.body).to.have.property('success', true);
+ });
+
+ it('it should remove the department and disassociate the rooms from it', async () => {
+ const { department } = await createDepartmentWithAnOnlineAgent();
+ const newVisitor = await createVisitor(department._id);
+ const newRoom = await createLivechatRoom(newVisitor.token);
+
+ const resp: Response = await request
+ .delete(api(`livechat/department/${department._id}`))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(200);
+
+ expect(resp.body).to.have.property('success', true);
+
+ const latestRoom = await getLivechatRoomInfo(newRoom._id);
+ expect(latestRoom.departmentId).to.be.undefined;
+ });
+
+ (IS_EE ? it : it.skip)('it should remove the department and disassociate the rooms from it which have its units', async () => {
+ const { department } = await createDepartmentWithAnOnlineAgent();
+ const newVisitor = await createVisitor(department._id);
+ const newRoom = await createLivechatRoom(newVisitor.token);
+
+ const monitor = await createUser();
+ await createMonitor(monitor.username);
+ const unit = await createUnit(monitor._id, monitor.username, [department._id]);
+
+ // except the room to have the unit
+ let latestRoom = await getLivechatRoomInfo(newRoom._id);
+ expect(latestRoom.departmentId).to.be.equal(department._id);
+ expect(latestRoom.departmentAncestors).to.be.an('array').that.includes(unit._id);
+
+ const resp: Response = await request
+ .delete(api(`livechat/department/${department._id}`))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(200);
+
+ expect(resp.body).to.have.property('success', true);
+
+ latestRoom = await getLivechatRoomInfo(newRoom._id);
+ expect(latestRoom.departmentId).to.be.undefined;
+ expect(latestRoom.departmentAncestors).to.be.undefined;
+ });
+
+ (IS_EE ? it : it.skip)(
+ 'contd from above test case: if a unit has more than 1 dept, then it should not disassociate rooms from other dept when any one dept is removed',
+ async () => {
+ const { department: department1 } = await createDepartmentWithAnOnlineAgent();
+ const newVisitor1 = await createVisitor(department1._id);
+ const newRoom1 = await createLivechatRoom(newVisitor1.token);
+
+ const { department: department2 } = await createDepartmentWithAnOnlineAgent();
+ const newVisitor2 = await createVisitor(department2._id);
+ const newRoom2 = await createLivechatRoom(newVisitor2.token);
+
+ const monitor = await createUser();
+ await createMonitor(monitor.username);
+ const unit = await createUnit(monitor._id, monitor.username, [department1._id, department2._id]);
+
+ // except the room to have the unit
+ let latestRoom1 = await getLivechatRoomInfo(newRoom1._id);
+ let latestRoom2 = await getLivechatRoomInfo(newRoom2._id);
+ expect(latestRoom1.departmentId).to.be.equal(department1._id);
+ expect(latestRoom1.departmentAncestors).to.be.an('array').that.includes(unit._id);
+ expect(latestRoom2.departmentId).to.be.equal(department2._id);
+ expect(latestRoom2.departmentAncestors).to.be.an('array').that.includes(unit._id);
+
+ const resp: Response = await request
+ .delete(api(`livechat/department/${department1._id}`))
+ .set(credentials)
+ .expect('Content-Type', 'application/json')
+ .expect(200);
+
+ expect(resp.body).to.have.property('success', true);
+
+ latestRoom1 = await getLivechatRoomInfo(newRoom1._id);
+ expect(latestRoom1.departmentId).to.be.undefined;
+ expect(latestRoom1.departmentAncestors).to.be.undefined;
+
+ latestRoom2 = await getLivechatRoomInfo(newRoom2._id);
+ expect(latestRoom2.departmentId).to.be.equal(department2._id);
+ expect(latestRoom2.departmentAncestors).to.be.an('array').that.includes(unit._id);
+ },
+ );
+ });
+
describe('GET livechat/department.autocomplete', () => {
it('should return an error when the user does not have the necessary permission', (done) => {
updatePermission('view-livechat-departments', [])
diff --git a/apps/meteor/tests/end-to-end/api/livechat/14-units.ts b/apps/meteor/tests/end-to-end/api/livechat/14-units.ts
index 76bbe33001c2..ef624febce29 100644
--- a/apps/meteor/tests/end-to-end/api/livechat/14-units.ts
+++ b/apps/meteor/tests/end-to-end/api/livechat/14-units.ts
@@ -40,7 +40,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request.get(api('livechat/units')).set(credentials).expect(200);
expect(body.units).to.be.an('array').with.lengthOf.greaterThan(0);
@@ -104,7 +104,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.get(api(`livechat/units/${unit._id}`))
@@ -128,7 +128,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.post(api(`livechat/units/${unit._id}`))
@@ -159,7 +159,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.delete(api(`livechat/units/${unit._id}`))
@@ -180,7 +180,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.get(api(`livechat/units/${unit._id}/departments`))
@@ -204,7 +204,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.get(api(`livechat/units/${unit._id}/departments/available`))
@@ -229,7 +229,7 @@ import { IS_EE } from '../../../e2e/config/constants';
const user = await createUser();
await createMonitor(user.username);
const department = await createDepartment();
- const unit = await createUnit(user._id, user.username, department._id);
+ const unit = await createUnit(user._id, user.username, [department._id]);
const { body } = await request
.get(api(`livechat/units/${unit._id}/monitors`))
diff --git a/packages/core-typings/src/IRoom.ts b/packages/core-typings/src/IRoom.ts
index b44fd8b8b5be..3250254a0946 100644
--- a/packages/core-typings/src/IRoom.ts
+++ b/packages/core-typings/src/IRoom.ts
@@ -188,6 +188,8 @@ export interface IOmnichannelGenericRoom extends Omit): FindCursor;
findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): [];
+ removeByDepartmentId(departmentId: string): Promise;
}
diff --git a/packages/model-typings/src/models/ILivechatDepartmentModel.ts b/packages/model-typings/src/models/ILivechatDepartmentModel.ts
index b75f93007982..41c97bd2ce7e 100644
--- a/packages/model-typings/src/models/ILivechatDepartmentModel.ts
+++ b/packages/model-typings/src/models/ILivechatDepartmentModel.ts
@@ -30,4 +30,6 @@ export interface ILivechatDepartmentModel extends IBaseModel;
removeBusinessHourFromDepartmentsByBusinessHourId(businessHourId: string): Promise;
+
+ unsetFallbackDepartmentByDepartmentId(departmentId: string): Promise;
}
diff --git a/packages/model-typings/src/models/ILivechatRoomsModel.ts b/packages/model-typings/src/models/ILivechatRoomsModel.ts
index b3607e29b930..4e7f36b30044 100644
--- a/packages/model-typings/src/models/ILivechatRoomsModel.ts
+++ b/packages/model-typings/src/models/ILivechatRoomsModel.ts
@@ -109,4 +109,6 @@ export interface ILivechatRoomsModel extends IBaseModel {
setAutoTransferredAtById(roomId: string): Promise;
findAvailableSources(): AggregationCursor;
+
+ bulkRemoveDepartmentAndUnitsFromRooms(departmentId: string): Promise;
}