@nocobase/plugin-departments 2.0.0-alpha.48 → 2.0.0-alpha.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -81,35 +81,21 @@ const setDepartments = async (ctx, next) => {
81
81
  const owners = departments.filter((department) => department.isOwner);
82
82
  await ctx.db.sequelize.transaction(async (t) => {
83
83
  await user.setDepartments(departmentIds, {
84
- through: {
85
- isMain: false,
86
- isOwner: false
87
- },
84
+ through: { isOwner: false },
85
+ transaction: t
86
+ });
87
+ await repo.update({
88
+ filterByTk: userId,
89
+ values: { mainDepartmentId: main ? main.id : null },
88
90
  transaction: t
89
91
  });
90
- if (main) {
91
- await throughRepo.update({
92
- filter: {
93
- userId,
94
- departmentId: main.id
95
- },
96
- values: {
97
- isMain: true
98
- },
99
- transaction: t
100
- });
101
- }
102
92
  if (owners.length) {
103
93
  await throughRepo.update({
104
94
  filter: {
105
95
  userId,
106
- departmentId: {
107
- $in: owners.map((owner) => owner.id)
108
- }
109
- },
110
- values: {
111
- isOwner: true
96
+ departmentId: { $in: owners.map((o) => o.id) }
112
97
  },
98
+ values: { isOwner: true },
113
99
  transaction: t
114
100
  });
115
101
  }
@@ -118,28 +104,28 @@ const setDepartments = async (ctx, next) => {
118
104
  };
119
105
  const setMainDepartment = async (ctx, next) => {
120
106
  const { userId, departmentId } = ctx.action.params.values || {};
107
+ const repo = ctx.db.getRepository("users");
121
108
  const throughRepo = ctx.db.getRepository("departmentsUsers");
122
109
  await ctx.db.sequelize.transaction(async (t) => {
123
- await throughRepo.update({
124
- filter: {
125
- userId,
126
- isMain: true
127
- },
128
- values: {
129
- isMain: false
130
- },
110
+ await repo.update({
111
+ filterByTk: userId,
112
+ values: { mainDepartmentId: departmentId },
131
113
  transaction: t
132
114
  });
133
- await throughRepo.update({
134
- filter: {
135
- userId,
136
- departmentId
137
- },
138
- values: {
139
- isMain: true
140
- },
115
+ const existingAssoc = await throughRepo.findOne({
116
+ filter: { userId, departmentId },
141
117
  transaction: t
142
118
  });
119
+ if (!existingAssoc) {
120
+ await throughRepo.create({
121
+ values: {
122
+ userId,
123
+ departmentId,
124
+ isOwner: false
125
+ },
126
+ transaction: t
127
+ });
128
+ }
143
129
  });
144
130
  await next();
145
131
  };
@@ -38,14 +38,16 @@ var departmentsUsers_default = (0, import_database.defineCollection)({
38
38
  {
39
39
  type: "boolean",
40
40
  name: "isOwner",
41
- // Weather the user is the owner of the department
42
41
  allowNull: false,
43
42
  defaultValue: false
44
43
  },
44
+ /**
45
+ * @deprecated
46
+ * use `user.mainDepartmentId` instead
47
+ */
45
48
  {
46
49
  type: "boolean",
47
50
  name: "isMain",
48
- // Weather this is the main department of the user
49
51
  allowNull: false,
50
52
  defaultValue: false
51
53
  }
@@ -38,14 +38,9 @@ export declare const mainDepartmentField: {
38
38
  name: string;
39
39
  target: string;
40
40
  foreignKey: string;
41
- otherKey: string;
42
41
  onDelete: string;
43
42
  sourceKey: string;
44
43
  targetKey: string;
45
- through: string;
46
- throughScope: {
47
- isMain: boolean;
48
- };
49
44
  uiSchema: {
50
45
  type: string;
51
46
  title: string;
@@ -59,6 +54,18 @@ export declare const mainDepartmentField: {
59
54
  };
60
55
  };
61
56
  };
57
+ export declare const mainDepartmentIdField: {
58
+ collectionName: string;
59
+ interface: string;
60
+ type: string;
61
+ name: string;
62
+ uiSchema: {
63
+ type: string;
64
+ title: string;
65
+ 'x-component': string;
66
+ 'x-read-pretty': boolean;
67
+ };
68
+ };
62
69
  declare const _default: {
63
70
  collectionOptions: import("@nocobase/database").CollectionOptions;
64
71
  mergeOptions: import("deepmerge").Options;
@@ -28,7 +28,8 @@ var users_exports = {};
28
28
  __export(users_exports, {
29
29
  default: () => users_default,
30
30
  departmentsField: () => departmentsField,
31
- mainDepartmentField: () => mainDepartmentField
31
+ mainDepartmentField: () => mainDepartmentField,
32
+ mainDepartmentIdField: () => mainDepartmentIdField
32
33
  });
33
34
  module.exports = __toCommonJS(users_exports);
34
35
  var import_database = require("@nocobase/database");
@@ -52,45 +53,53 @@ const departmentsField = {
52
53
  multiple: true,
53
54
  fieldNames: {
54
55
  label: "title",
55
- value: "name"
56
+ value: "id"
56
57
  }
57
58
  }
58
59
  }
59
60
  };
60
61
  const mainDepartmentField = {
61
62
  collectionName: "users",
62
- interface: "m2m",
63
- type: "belongsToMany",
63
+ interface: "m2o",
64
+ type: "belongsTo",
64
65
  name: "mainDepartment",
65
66
  target: "departments",
66
- foreignKey: "userId",
67
- otherKey: "departmentId",
68
- onDelete: "CASCADE",
67
+ foreignKey: "mainDepartmentId",
68
+ onDelete: "SET NULL",
69
69
  sourceKey: "id",
70
70
  targetKey: "id",
71
- through: "departmentsUsers",
72
- throughScope: {
73
- isMain: true
74
- },
75
71
  uiSchema: {
76
- type: "m2m",
72
+ type: "string",
77
73
  title: '{{t("Main department")}}',
78
- "x-component": "UserMainDepartmentField",
74
+ "x-component": "AssociationField",
79
75
  "x-component-props": {
80
76
  multiple: false,
81
77
  fieldNames: {
82
78
  label: "title",
83
- value: "name"
79
+ value: "id"
84
80
  }
85
81
  }
86
82
  }
87
83
  };
84
+ const mainDepartmentIdField = {
85
+ collectionName: "users",
86
+ interface: "inputNumber",
87
+ type: "bigInt",
88
+ name: "mainDepartmentId",
89
+ uiSchema: {
90
+ type: "number",
91
+ title: "mainDepartmentId",
92
+ "x-component": "InputNumber",
93
+ "x-read-pretty": true
94
+ }
95
+ };
88
96
  var users_default = (0, import_database.extendCollection)({
89
97
  name: "users",
90
- fields: [departmentsField, mainDepartmentField]
98
+ fields: [departmentsField, mainDepartmentField, mainDepartmentIdField]
91
99
  });
92
100
  // Annotate the CommonJS export names for ESM import in node:
93
101
  0 && (module.exports = {
94
102
  departmentsField,
95
- mainDepartmentField
103
+ mainDepartmentField,
104
+ mainDepartmentIdField
96
105
  });
@@ -177,6 +177,10 @@ class DepartmentDataSyncResource extends import_plugin_user_data_sync.UserDataRe
177
177
  if (userDepartments.length) {
178
178
  await user.removeDepartments(userDepartments);
179
179
  }
180
+ await this.userRepo.update({
181
+ filterByTk: user.id,
182
+ values: { mainDepartmentId: null }
183
+ });
180
184
  if (currentDepartmentIds && currentDepartmentIds.length) {
181
185
  return currentDepartmentIds.map((id) => ({ resourcesPk: id, isDeleted: true }));
182
186
  } else {
@@ -190,9 +194,7 @@ class DepartmentDataSyncResource extends import_plugin_user_data_sync.UserDataRe
190
194
  return sourceDepartment.uid;
191
195
  });
192
196
  const newDepartmentIds = await this.getDepartmentIdsBySourceUks(sourceDepartmentIds, sourceName);
193
- const newDepartments = await this.deptRepo.find({
194
- filter: { id: { $in: newDepartmentIds } }
195
- });
197
+ const newDepartments = await this.deptRepo.find({ filter: { id: { $in: newDepartmentIds } } });
196
198
  const realCurrentDepartments = await user.getDepartments();
197
199
  const toRealRemoveDepartments = realCurrentDepartments.filter((currnetDepartment) => {
198
200
  return !newDepartments.find((newDepartment) => newDepartment.id === currnetDepartment.id);
@@ -209,6 +211,7 @@ class DepartmentDataSyncResource extends import_plugin_user_data_sync.UserDataRe
209
211
  if (toRealAddDepartments.length) {
210
212
  await user.addDepartments(toRealAddDepartments);
211
213
  }
214
+ let mainDepartmentId = null;
212
215
  for (const sourceDepartment of sourceDepartments) {
213
216
  this.logger.debug("update dept owner: " + JSON.stringify(sourceDepartment));
214
217
  let isOwner = false;
@@ -222,47 +225,41 @@ class DepartmentDataSyncResource extends import_plugin_user_data_sync.UserDataRe
222
225
  uid = sourceDepartment;
223
226
  }
224
227
  const deptId = await this.getDepartmentIdBySourceUk(uid, sourceName);
225
- this.logger.debug("update dept owner: " + JSON.stringify({ deptId, isOwner, isMain, userId: user.id }));
228
+ this.logger.debug(
229
+ "update dept owner: " + JSON.stringify({ deptId, isOwner, mainDepartmentId: isMain ? deptId : null, userId: user.id })
230
+ );
226
231
  if (!deptId) {
227
232
  continue;
228
233
  }
229
234
  await this.deptUserRepo.update({
230
- filter: {
231
- userId: user.id,
232
- departmentId: deptId
233
- },
234
- values: {
235
- isOwner,
236
- isMain
237
- }
235
+ filter: { userId: user.id, departmentId: deptId },
236
+ values: { isOwner }
238
237
  });
238
+ if (isMain) {
239
+ mainDepartmentId = deptId;
240
+ }
239
241
  }
242
+ await this.userRepo.update({
243
+ filterByTk: user.id,
244
+ values: { mainDepartmentId }
245
+ });
240
246
  const recordResourceChangeds = [];
241
247
  if (currentDepartmentIds !== void 0 && currentDepartmentIds.length > 0) {
242
248
  const toRemoveDepartmentIds = currentDepartmentIds.filter(
243
249
  (currentDepartmentId) => !newDepartmentIds.includes(currentDepartmentId)
244
250
  );
245
251
  recordResourceChangeds.push(
246
- ...toRemoveDepartmentIds.map((departmentId) => {
247
- return { resourcesPk: departmentId, isDeleted: true };
248
- })
252
+ ...toRemoveDepartmentIds.map((departmentId) => ({ resourcesPk: departmentId, isDeleted: true }))
249
253
  );
250
254
  const toAddDepartmentIds = newDepartmentIds.filter(
251
255
  (newDepartmentId) => !currentDepartmentIds.includes(newDepartmentId)
252
256
  );
253
257
  recordResourceChangeds.push(
254
- ...toAddDepartmentIds.map((departmentId) => {
255
- return { resourcesPk: departmentId, isDeleted: false };
256
- })
258
+ ...toAddDepartmentIds.map((departmentId) => ({ resourcesPk: departmentId, isDeleted: false }))
257
259
  );
258
260
  } else {
259
261
  recordResourceChangeds.push(
260
- ...toRealAddDepartments.map((department) => {
261
- return {
262
- resourcesPk: department.id,
263
- isDeleted: false
264
- };
265
- })
262
+ ...toRealAddDepartments.map((department) => ({ resourcesPk: department.id, isDeleted: false }))
266
263
  );
267
264
  }
268
265
  return recordResourceChangeds;
@@ -47,7 +47,7 @@ const setDepartmentsInfo = async (ctx, next) => {
47
47
  return next();
48
48
  }
49
49
  ctx.state.currentUser.departments = departments;
50
- ctx.state.currentUser.mainDeparmtent = departments.find((dept) => dept.isMain);
50
+ ctx.state.currentUser.mainDeparmtent = departments.find((dept) => dept.id === currentUser.mainDepartmentId);
51
51
  const departmentIds = departments.map((dept) => dept.id);
52
52
  const roleRepo = ctx.db.getRepository("roles");
53
53
  const roles = await roleRepo.find({
@@ -30,80 +30,70 @@ __export(set_main_department_exports, {
30
30
  });
31
31
  module.exports = __toCommonJS(set_main_department_exports);
32
32
  const setMainDepartment = async (ctx, next) => {
33
+ var _a, _b;
33
34
  await next();
34
- const { associatedName, resourceName, associatedIndex, actionName, values } = ctx.action.params;
35
+ const { associatedName, resourceName, associatedIndex, actionName, values, filterByTk } = ctx.action.params;
35
36
  if (associatedName === "departments" && resourceName === "members" && (values == null ? void 0 : values.length)) {
37
+ const userRepo = ctx.db.getRepository("users");
36
38
  const throughRepo = ctx.db.getRepository("departmentsUsers");
37
- const usersHasMain = await throughRepo.find({
38
- filter: {
39
- userId: {
40
- $in: values
41
- },
42
- isMain: true
43
- }
44
- });
45
- const userIdsHasMain = usersHasMain.map((item) => item.userId);
46
39
  if (actionName === "add" || actionName === "set") {
47
- await throughRepo.update({
48
- filter: {
49
- userId: {
50
- $in: values.filter((id) => !userIdsHasMain.includes(id))
51
- },
52
- departmentId: associatedIndex
53
- },
54
- values: {
55
- isMain: true
40
+ for (const userId of values) {
41
+ const user = await userRepo.findOne({ filterByTk: userId, fields: ["id", "mainDepartmentId"] });
42
+ if (!(user == null ? void 0 : user.mainDepartmentId)) {
43
+ await userRepo.update({
44
+ filterByTk: userId,
45
+ values: { mainDepartmentId: associatedIndex }
46
+ });
56
47
  }
57
- });
48
+ }
58
49
  return;
59
50
  }
60
51
  if (actionName === "remove") {
61
- const userIdsHasNoMain = values.filter((id) => !userIdsHasMain.includes(id));
62
- for (const userId of userIdsHasNoMain) {
63
- const firstDept = await throughRepo.findOne({
64
- filter: {
65
- userId
66
- }
67
- });
68
- if (firstDept) {
69
- await throughRepo.update({
70
- filter: {
71
- userId,
72
- departmentId: firstDept.departmentId
73
- },
74
- values: {
75
- isMain: true
76
- }
52
+ for (const userId of values) {
53
+ const user = await userRepo.findOne({ filterByTk: userId, fields: ["id", "mainDepartmentId"] });
54
+ if ((user == null ? void 0 : user.mainDepartmentId) === Number(associatedIndex)) {
55
+ const firstDept = await throughRepo.findOne({
56
+ filter: { userId }
57
+ });
58
+ await userRepo.update({
59
+ filterByTk: userId,
60
+ values: { mainDepartmentId: firstDept ? firstDept.departmentId : null }
77
61
  });
78
62
  }
79
63
  }
80
64
  }
81
65
  }
82
66
  if (associatedName === "users" && resourceName === "departments" && ["add", "remove", "set"].includes(actionName)) {
67
+ const userRepo = ctx.db.getRepository("users");
83
68
  const throughRepo = ctx.db.getRepository("departmentsUsers");
84
- const hasMain = await throughRepo.findOne({
85
- filter: {
86
- userId: associatedIndex,
87
- isMain: true
88
- }
89
- });
90
- if (hasMain) {
91
- return;
69
+ const user = await userRepo.findOne({ filterByTk: associatedIndex, fields: ["id", "mainDepartmentId"] });
70
+ let hasValidMain = false;
71
+ if (user == null ? void 0 : user.mainDepartmentId) {
72
+ const existingAssoc = await throughRepo.findOne({
73
+ filter: { userId: associatedIndex, departmentId: user.mainDepartmentId }
74
+ });
75
+ hasValidMain = !!existingAssoc;
92
76
  }
93
- const firstDept = await throughRepo.findOne({
94
- filter: {
95
- userId: associatedIndex
96
- }
97
- });
98
- if (firstDept) {
99
- await throughRepo.update({
100
- filter: {
101
- userId: associatedIndex,
102
- departmentId: firstDept.departmentId
103
- },
104
- values: {
105
- isMain: true
106
- }
77
+ if (!hasValidMain) {
78
+ const firstDept = await throughRepo.findOne({
79
+ filter: { userId: associatedIndex }
80
+ });
81
+ await userRepo.update({
82
+ filterByTk: associatedIndex,
83
+ values: { mainDepartmentId: firstDept ? firstDept.departmentId : null }
84
+ });
85
+ }
86
+ }
87
+ if (resourceName === "users" && actionName === "update") {
88
+ const userRepo = ctx.db.getRepository("users");
89
+ const throughRepo = ctx.db.getRepository("departmentsUsers");
90
+ const userId = associatedIndex ?? filterByTk ?? ((_b = (_a = ctx.body) == null ? void 0 : _a.data) == null ? void 0 : _b.id);
91
+ if (!userId) return;
92
+ const deptUsers = await throughRepo.find({ filter: { userId } });
93
+ if (deptUsers.length === 1) {
94
+ await userRepo.update({
95
+ filterByTk: userId,
96
+ values: { mainDepartmentId: deptUsers[0].departmentId }
107
97
  });
108
98
  }
109
99
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Migration } from '@nocobase/server';
10
+ export default class extends Migration {
11
+ on: string;
12
+ appVersion: string;
13
+ up(): Promise<void>;
14
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var migrate_main_department_id_20250828100101_exports = {};
28
+ __export(migrate_main_department_id_20250828100101_exports, {
29
+ default: () => migrate_main_department_id_20250828100101_default
30
+ });
31
+ module.exports = __toCommonJS(migrate_main_department_id_20250828100101_exports);
32
+ var import_server = require("@nocobase/server");
33
+ class migrate_main_department_id_20250828100101_default extends import_server.Migration {
34
+ on = "afterLoad";
35
+ appVersion = "<2.0.0";
36
+ async up() {
37
+ await this.db.sequelize.transaction(async (transaction) => {
38
+ var _a, _b;
39
+ const users = this.db.getCollection("users");
40
+ const departments = this.db.getCollection("departments");
41
+ if (!users || !departments) {
42
+ return;
43
+ }
44
+ const fieldRepo = this.db.getRepository("fields");
45
+ if (!fieldRepo) {
46
+ return;
47
+ }
48
+ const currentMainField = await fieldRepo.findOne({
49
+ filter: {
50
+ collectionName: "users",
51
+ name: "mainDepartment"
52
+ },
53
+ transaction
54
+ });
55
+ const iface = (_a = currentMainField == null ? void 0 : currentMainField.get) == null ? void 0 : _a.call(currentMainField, "interface");
56
+ const type = (_b = currentMainField == null ? void 0 : currentMainField.get) == null ? void 0 : _b.call(currentMainField, "type");
57
+ const shouldMigrate = !currentMainField || type === "belongsToMany" || iface === "m2m";
58
+ if (!shouldMigrate) {
59
+ return;
60
+ }
61
+ const rows = await this.db.getRepository("departmentsUsers").find({
62
+ filter: {
63
+ isMain: true
64
+ },
65
+ fields: ["userId", "departmentId"],
66
+ transaction
67
+ });
68
+ for (const row of rows) {
69
+ await this.db.getRepository("users").update({
70
+ filter: {
71
+ id: row.userId
72
+ },
73
+ values: {
74
+ mainDepartmentId: row.departmentId
75
+ },
76
+ transaction,
77
+ hooks: false
78
+ });
79
+ }
80
+ await fieldRepo.destroy({
81
+ filter: {
82
+ collectionName: "departmentsUsers",
83
+ name: "isMain"
84
+ },
85
+ transaction
86
+ });
87
+ const mainDepartmentField = await fieldRepo.findOne({
88
+ filter: {
89
+ collectionName: "users",
90
+ name: "mainDepartment"
91
+ },
92
+ transaction
93
+ });
94
+ if (mainDepartmentField) {
95
+ const nextOptions = {
96
+ ...mainDepartmentField.options || {},
97
+ target: "departments",
98
+ foreignKey: "mainDepartmentId",
99
+ onDelete: "SET NULL",
100
+ sourceKey: "id",
101
+ targetKey: "id",
102
+ uiSchema: {
103
+ type: "string",
104
+ title: '{{t("Main department")}}',
105
+ "x-component": "AssociationField",
106
+ "x-component-props": {
107
+ multiple: false,
108
+ fieldNames: {
109
+ label: "title",
110
+ value: "id"
111
+ }
112
+ }
113
+ }
114
+ };
115
+ delete nextOptions.through;
116
+ delete nextOptions.otherKey;
117
+ delete nextOptions.throughScope;
118
+ await fieldRepo.update({
119
+ filter: {
120
+ collectionName: "users",
121
+ name: "mainDepartment"
122
+ },
123
+ values: {
124
+ type: "belongsTo",
125
+ interface: "m2o",
126
+ options: nextOptions
127
+ },
128
+ transaction
129
+ });
130
+ }
131
+ });
132
+ }
133
+ }
@@ -15,5 +15,6 @@ export declare class PluginDepartmentsServer extends Plugin {
15
15
  afterEnable(): Promise<void>;
16
16
  afterDisable(): Promise<void>;
17
17
  remove(): Promise<void>;
18
+ registerErrorHandler(): void;
18
19
  }
19
20
  export default PluginDepartmentsServer;