@things-factory/organization 8.0.0-beta.8 → 8.0.0

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.
Files changed (94) hide show
  1. package/client/bootstrap.ts +23 -0
  2. package/client/component/approval-line-brief.ts +119 -0
  3. package/client/component/approval-line-items-editor-popup.ts +91 -0
  4. package/client/component/approval-line-items-editor.ts +325 -0
  5. package/client/component/approval-line-selector.ts +235 -0
  6. package/client/component/approval-line-templates-manager.ts +229 -0
  7. package/client/component/approval-line-view.ts +122 -0
  8. package/client/component/assignees-editor-popup.ts +79 -0
  9. package/client/component/assignees-editor.ts +217 -0
  10. package/client/component/assignees-view.ts +55 -0
  11. package/client/component/department-selector.ts +151 -0
  12. package/client/component/department-view.ts +107 -0
  13. package/client/component/index.ts +16 -0
  14. package/client/component/recipients-editor-popup.ts +79 -0
  15. package/client/component/recipients-editor.ts +212 -0
  16. package/client/component/recipients-view.ts +55 -0
  17. package/client/grist-editor/grist-editor-approval-line.ts +70 -0
  18. package/client/grist-editor/grist-editor-assignees.ts +69 -0
  19. package/client/grist-editor/grist-editor-department-object.ts +78 -0
  20. package/client/grist-editor/grist-editor-recipients.ts +69 -0
  21. package/client/grist-editor/grist-renderer-approval-line.ts +13 -0
  22. package/client/grist-editor/grist-renderer-assignees.ts +13 -0
  23. package/client/grist-editor/grist-renderer-department-object.ts +13 -0
  24. package/client/grist-editor/grist-renderer-recipients.ts +13 -0
  25. package/client/index.ts +2 -0
  26. package/client/pages/approval-line/common-approval-line-templates-page.ts +382 -0
  27. package/client/pages/approval-line/my-approval-line-templates-page.ts +385 -0
  28. package/client/pages/department/department-importer.ts +87 -0
  29. package/client/pages/department/department-list-page.ts +450 -0
  30. package/client/pages/department/department-tree-page.ts +379 -0
  31. package/client/pages/employee/employee-importer.ts +87 -0
  32. package/client/pages/employee/employee-list-page.ts +772 -0
  33. package/client/pages/employee/employees-by-department.ts +519 -0
  34. package/client/route.ts +27 -0
  35. package/client/tsconfig.json +13 -0
  36. package/client/types/approval-line.ts +52 -0
  37. package/client/types/contact.ts +51 -0
  38. package/client/types/department.ts +29 -0
  39. package/client/types/employee.ts +50 -0
  40. package/client/types/index.ts +5 -0
  41. package/client/types/org-member.ts +27 -0
  42. package/dist-client/bootstrap.js +1 -8
  43. package/dist-client/bootstrap.js.map +1 -1
  44. package/dist-client/pages/employee/employee-list-page.js +3 -3
  45. package/dist-client/pages/employee/employee-list-page.js.map +1 -1
  46. package/dist-client/pages/employee/employees-by-department.js +2 -2
  47. package/dist-client/pages/employee/employees-by-department.js.map +1 -1
  48. package/dist-client/tsconfig.tsbuildinfo +1 -1
  49. package/dist-server/service/employee/employee-history.d.ts +2 -6
  50. package/dist-server/service/employee/employee-history.js +3 -23
  51. package/dist-server/service/employee/employee-history.js.map +1 -1
  52. package/dist-server/service/employee/employee-query.js +1 -1
  53. package/dist-server/service/employee/employee-query.js.map +1 -1
  54. package/dist-server/service/employee/employee-type.d.ts +5 -13
  55. package/dist-server/service/employee/employee-type.js +7 -39
  56. package/dist-server/service/employee/employee-type.js.map +1 -1
  57. package/dist-server/service/employee/employee.d.ts +2 -6
  58. package/dist-server/service/employee/employee.js +3 -23
  59. package/dist-server/service/employee/employee.js.map +1 -1
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +12 -12
  62. package/server/controllers/register-employee-as-system-user.ts +136 -0
  63. package/server/index.ts +3 -0
  64. package/server/migrations/1723861013111-seed-organization-codes.ts +127 -0
  65. package/server/migrations/index.ts +9 -0
  66. package/server/routes.ts +26 -0
  67. package/server/service/approval-line/approval-line-item.ts +42 -0
  68. package/server/service/approval-line/approval-line-mutation.ts +394 -0
  69. package/server/service/approval-line/approval-line-query.ts +208 -0
  70. package/server/service/approval-line/approval-line-type.ts +63 -0
  71. package/server/service/approval-line/approval-line.ts +123 -0
  72. package/server/service/approval-line/index.ts +7 -0
  73. package/server/service/department/department-history.ts +141 -0
  74. package/server/service/department/department-mutation.ts +231 -0
  75. package/server/service/department/department-query.ts +131 -0
  76. package/server/service/department/department-type.ts +74 -0
  77. package/server/service/department/department.ts +116 -0
  78. package/server/service/department/event-subscriber.ts +17 -0
  79. package/server/service/department/index.ts +9 -0
  80. package/server/service/employee/employee-history.ts +173 -0
  81. package/server/service/employee/employee-mutation.ts +386 -0
  82. package/server/service/employee/employee-query.ts +172 -0
  83. package/server/service/employee/employee-type.ts +176 -0
  84. package/server/service/employee/employee.ts +177 -0
  85. package/server/service/employee/event-subscriber.ts +17 -0
  86. package/server/service/employee/index.ts +9 -0
  87. package/server/service/index.ts +39 -0
  88. package/server/tsconfig.json +10 -0
  89. package/dist-client/filters-form/filter-department-object.d.ts +0 -3
  90. package/dist-client/filters-form/filter-department-object.js +0 -8
  91. package/dist-client/filters-form/filter-department-object.js.map +0 -1
  92. package/dist-client/filters-form/ox-filter-department-object.d.ts +0 -15
  93. package/dist-client/filters-form/ox-filter-department-object.js +0 -130
  94. package/dist-client/filters-form/ox-filter-department-object.js.map +0 -1
@@ -0,0 +1,116 @@
1
+ import {
2
+ CreateDateColumn,
3
+ UpdateDateColumn,
4
+ DeleteDateColumn,
5
+ Entity,
6
+ Index,
7
+ Column,
8
+ RelationId,
9
+ ManyToOne,
10
+ OneToMany,
11
+ PrimaryGeneratedColumn,
12
+ VersionColumn
13
+ } from 'typeorm'
14
+ import { ObjectType, Field, Int, ID, registerEnumType } from 'type-graphql'
15
+
16
+ import { Domain } from '@things-factory/shell'
17
+ import { User } from '@things-factory/auth-base'
18
+ import { Employee } from '../employee/employee'
19
+
20
+ @Entity()
21
+ @Index('ix_department_0', (department: Department) => [department.domain, department.name], {
22
+ unique: true,
23
+ where: '"deleted_at" IS NULL'
24
+ })
25
+ @Index('ix_department_1', (department: Department) => [department.domain, department.controlNo], {
26
+ unique: true,
27
+ where: '"deleted_at" IS NULL'
28
+ })
29
+ @ObjectType({ description: 'Entity for Department' })
30
+ export class Department {
31
+ @PrimaryGeneratedColumn('uuid')
32
+ @Field(type => ID)
33
+ readonly id: string
34
+
35
+ @VersionColumn()
36
+ @Field({ nullable: true })
37
+ version?: number = 1
38
+
39
+ @ManyToOne(type => Domain)
40
+ @Field(type => Domain)
41
+ domain?: Domain
42
+
43
+ @RelationId((department: Department) => department.domain)
44
+ domainId?: string
45
+
46
+ @Column()
47
+ @Field()
48
+ controlNo: string
49
+
50
+ @Column()
51
+ @Field({ nullable: true })
52
+ name?: string
53
+
54
+ @Column({ nullable: true })
55
+ @Field({ nullable: true })
56
+ description?: string
57
+
58
+ @ManyToOne(type => Department, department => department.children, { nullable: true })
59
+ @Field({ nullable: true })
60
+ parent?: Department
61
+
62
+ @RelationId((department: Department) => department.parent)
63
+ parentId?: string
64
+
65
+ @OneToMany(type => Department, department => department.parent)
66
+ @Field(type => [Department])
67
+ children?: Department[]
68
+
69
+ @OneToMany(type => Employee, employee => employee.department)
70
+ @Field(type => [Employee])
71
+ members?: Employee[]
72
+
73
+ @ManyToOne(type => Employee, manager => manager.manages, { nullable: true })
74
+ @Field(type => Employee, { nullable: true })
75
+ manager?: Employee
76
+
77
+ @RelationId((department: Department) => department.manager)
78
+ managerId?: string
79
+
80
+ @Column({ nullable: true })
81
+ @Field({ nullable: true })
82
+ active?: boolean
83
+
84
+ @Column({ nullable: true })
85
+ @Field({ nullable: true })
86
+ extension?: string
87
+
88
+ @Field(type => String, { nullable: true })
89
+ picture?: string
90
+
91
+ @CreateDateColumn()
92
+ @Field({ nullable: true })
93
+ createdAt?: Date
94
+
95
+ @UpdateDateColumn()
96
+ @Field({ nullable: true })
97
+ updatedAt?: Date
98
+
99
+ @DeleteDateColumn()
100
+ @Field({ nullable: true })
101
+ deletedAt?: Date
102
+
103
+ @ManyToOne(type => User, { nullable: true })
104
+ @Field(type => User, { nullable: true })
105
+ creator?: User
106
+
107
+ @RelationId((department: Department) => department.creator)
108
+ creatorId?: string
109
+
110
+ @ManyToOne(type => User, { nullable: true })
111
+ @Field(type => User, { nullable: true })
112
+ updater?: User
113
+
114
+ @RelationId((department: Department) => department.updater)
115
+ updaterId?: string
116
+ }
@@ -0,0 +1,17 @@
1
+ import { EventSubscriber } from 'typeorm'
2
+
3
+ import { HistoryEntitySubscriber } from '@operato/typeorm-history'
4
+
5
+ import { Department } from './department'
6
+ import { DepartmentHistory } from './department-history'
7
+
8
+ @EventSubscriber()
9
+ export class DepartmentHistoryEntitySubscriber extends HistoryEntitySubscriber<Department, DepartmentHistory> {
10
+ public get entity() {
11
+ return Department
12
+ }
13
+
14
+ public get historyEntity() {
15
+ return DepartmentHistory
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ import { Department } from './department'
2
+ import { DepartmentHistory } from './department-history'
3
+ import { DepartmentQuery } from './department-query'
4
+ import { DepartmentMutation } from './department-mutation'
5
+ import { DepartmentHistoryEntitySubscriber } from './event-subscriber'
6
+
7
+ export const entities = [Department, DepartmentHistory]
8
+ export const resolvers = [DepartmentQuery, DepartmentMutation]
9
+ export const subscribers = [DepartmentHistoryEntitySubscriber]
@@ -0,0 +1,173 @@
1
+ import { Field, ID, ObjectType } from 'type-graphql'
2
+ import { Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId } from 'typeorm'
3
+
4
+ import {
5
+ HistoryActionColumn,
6
+ HistoryActionType,
7
+ HistoryEntityInterface,
8
+ HistoryOriginalIdColumn
9
+ } from '@operato/typeorm-history'
10
+
11
+ import { User } from '@things-factory/auth-base'
12
+ import { Contact } from '@things-factory/contact'
13
+
14
+ import { config } from '@things-factory/env'
15
+ import { Domain } from '@things-factory/shell'
16
+
17
+ import { Employee, EmployeeType } from './employee'
18
+ import { Department } from '../department/department'
19
+
20
+ const ORMCONFIG = config.get('ormconfig', {})
21
+ const DATABASE_TYPE = ORMCONFIG.type
22
+
23
+ @Entity()
24
+ @Index(
25
+ 'ix_employee_history_0',
26
+ (employeeHistory: EmployeeHistory) => [employeeHistory.originalId, employeeHistory.version],
27
+ { unique: true }
28
+ )
29
+ @Index(
30
+ 'ix_employee_history_1',
31
+ (employeeHistory: EmployeeHistory) => [employeeHistory.domain, employeeHistory.originalId, employeeHistory.version],
32
+ { unique: true }
33
+ )
34
+ @ObjectType({ description: 'History Entity of Employee' })
35
+ export class EmployeeHistory implements HistoryEntityInterface<Employee> {
36
+ @PrimaryGeneratedColumn('uuid')
37
+ @Field(type => ID)
38
+ readonly id: string
39
+
40
+ @Column({ nullable: true, default: 1 })
41
+ @Field({ nullable: true })
42
+ version?: number = 1
43
+
44
+ @ManyToOne(type => Domain)
45
+ @Field(type => Domain)
46
+ domain?: Domain
47
+
48
+ @RelationId((employee: Employee) => employee.domain)
49
+ domainId?: string
50
+
51
+ @Column()
52
+ @Field()
53
+ controlNo: string
54
+
55
+ @Column()
56
+ @Field({ nullable: true })
57
+ name?: string
58
+
59
+ @Column({ nullable: true })
60
+ @Field({ nullable: true })
61
+ alias?: string
62
+
63
+ @Column({ nullable: true })
64
+ @Field({ nullable: true })
65
+ jobResponsibility?: string
66
+
67
+ @Column({ nullable: true })
68
+ @Field({ nullable: true })
69
+ jobPosition?: string
70
+
71
+ @Column({ nullable: true })
72
+ @Field({ nullable: true })
73
+ note?: string
74
+
75
+ @ManyToOne(type => Employee, supervisor => supervisor.supervises, { nullable: true })
76
+ @Field({ nullable: true })
77
+ supervisor?: Employee
78
+
79
+ @RelationId((employee: Employee) => employee.supervisor)
80
+ supervisorId?: string
81
+
82
+ @OneToMany(type => Employee, employee => employee.supervisor)
83
+ @Field(type => [Employee])
84
+ supervises?: Employee[]
85
+
86
+ @OneToMany(type => Department, department => department.manager)
87
+ @Field(type => [Department])
88
+ manages?: Department[]
89
+
90
+ @Column({ nullable: true })
91
+ @Field({ nullable: true })
92
+ active?: boolean
93
+
94
+ @Column({ nullable: true })
95
+ @Field({ nullable: true })
96
+ type?: EmployeeType
97
+
98
+ @Column({ nullable: true })
99
+ @Field({ nullable: true })
100
+ hiredOn?: string
101
+
102
+ @Column({ nullable: true })
103
+ @Field({ nullable: true })
104
+ retiredAt?: string
105
+
106
+ @Column({ nullable: true })
107
+ @Field({ nullable: true })
108
+ extension?: string
109
+
110
+ @Column({ nullable: true })
111
+ @Field({ nullable: true })
112
+ createdAt?: Date
113
+
114
+ @Column({ nullable: true })
115
+ @Field({ nullable: true })
116
+ updatedAt?: Date
117
+
118
+ @Column({ nullable: true })
119
+ @Field({ nullable: true })
120
+ deletedAt?: Date
121
+
122
+ @ManyToOne(type => User, { nullable: true })
123
+ @Field(type => User, { nullable: true })
124
+ creator?: User
125
+
126
+ @RelationId((employee: Employee) => employee.creator)
127
+ creatorId?: string
128
+
129
+ @ManyToOne(type => User, { nullable: true })
130
+ @Field(type => User, { nullable: true })
131
+ updater?: User
132
+
133
+ @RelationId((employee: Employee) => employee.updater)
134
+ updaterId?: string
135
+
136
+ @Field(type => String, { nullable: true })
137
+ photo?: string
138
+
139
+ @ManyToOne(type => User, { nullable: true })
140
+ @Field({ nullable: true })
141
+ user?: User
142
+
143
+ @RelationId((employee: Employee) => employee.user)
144
+ userId?: string
145
+
146
+ @ManyToOne(type => Contact, { nullable: true })
147
+ @Field({ nullable: true })
148
+ contact?: Contact
149
+
150
+ @RelationId((employee: Employee) => employee.contact)
151
+ contactId?: string
152
+
153
+ @HistoryOriginalIdColumn()
154
+ public originalId!: string
155
+
156
+ @HistoryActionColumn({
157
+ nullable: false,
158
+ type:
159
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
160
+ ? 'enum'
161
+ : DATABASE_TYPE == 'oracle'
162
+ ? 'varchar2'
163
+ : DATABASE_TYPE == 'mssql'
164
+ ? 'nvarchar'
165
+ : 'varchar',
166
+ enum:
167
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
168
+ ? HistoryActionType
169
+ : undefined,
170
+ length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 10
171
+ })
172
+ public action!: HistoryActionType
173
+ }
@@ -0,0 +1,386 @@
1
+ import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
2
+ import { In } from 'typeorm'
3
+
4
+ import { getRepository } from '@things-factory/shell'
5
+ import { User } from '@things-factory/auth-base'
6
+ import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
7
+ import { Contact } from '@things-factory/contact'
8
+
9
+ import { Employee } from './employee'
10
+ import { NewEmployee, EmployeePatch } from './employee-type'
11
+ import { Department } from '../department/department'
12
+
13
+ import {
14
+ onDeleteEmployeeAsSystemUser,
15
+ onUpdateEmployeeAsSystemUser,
16
+ registerEmployeeAsSystemUser
17
+ } from '../../controllers/register-employee-as-system-user'
18
+
19
+ @Resolver(Employee)
20
+ export class EmployeeMutation {
21
+ @Directive('@transaction')
22
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
23
+ @Mutation(returns => Employee, {
24
+ description:
25
+ 'Creates a new employee record with the provided details. Optionally associates the employee with a supervisor, department, and system user. If a photo is provided, it will be attached to the employee record.'
26
+ })
27
+ async createEmployee(@Arg('employee') employee: NewEmployee, @Ctx() context: ResolverContext): Promise<Employee> {
28
+ const { domain, user, tx } = context.state
29
+
30
+ const supervisor =
31
+ 'supervisor' in employee
32
+ ? employee.supervisor && (await getRepository(Employee, tx).findOneBy({ id: employee.supervisor.id }))
33
+ : undefined
34
+
35
+ const department =
36
+ 'department' in employee
37
+ ? employee.department && (await getRepository(Department, tx).findOneBy({ id: employee.department.id }))
38
+ : undefined
39
+
40
+ const connectedUser =
41
+ 'user' in employee
42
+ ? employee.user && (await getRepository(User, tx).findOneBy({ id: employee.user.id }))
43
+ : undefined
44
+
45
+ const result = await getRepository(Employee, tx).save({
46
+ ...employee,
47
+ supervisor,
48
+ department,
49
+ user: connectedUser,
50
+ domain,
51
+ creator: user,
52
+ updater: user
53
+ })
54
+
55
+ if (employee.photo) {
56
+ await createAttachment(
57
+ null,
58
+ {
59
+ attachment: {
60
+ file: employee.photo,
61
+ refType: Employee.name,
62
+ refBy: result.id
63
+ }
64
+ },
65
+ context
66
+ )
67
+ }
68
+
69
+ return result
70
+ }
71
+
72
+ @Directive('@transaction')
73
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
74
+ @Mutation(returns => Employee, {
75
+ description:
76
+ 'Updates the details of an existing employee identified by the given ID. Optionally updates the supervisor, department, system user, and photo associated with the employee.'
77
+ })
78
+ async updateEmployee(
79
+ @Arg('id') id: string,
80
+ @Arg('patch') patch: EmployeePatch,
81
+ @Ctx() context: ResolverContext
82
+ ): Promise<Employee> {
83
+ const { domain, user, tx } = context.state
84
+
85
+ const supervisor =
86
+ 'supervisor' in patch
87
+ ? patch.supervisor && (await getRepository(Employee, tx).findOneBy({ id: patch.supervisor.id }))
88
+ : undefined
89
+
90
+ const department =
91
+ 'department' in patch
92
+ ? patch.department && (await getRepository(Department, tx).findOneBy({ id: patch.department.id }))
93
+ : undefined
94
+
95
+ const connectedUser =
96
+ 'user' in patch ? patch.user && (await getRepository(User, tx).findOneBy({ id: patch.user.id })) : undefined
97
+
98
+ const repository = getRepository(Employee, tx)
99
+ const employee = await repository.findOne({
100
+ where: { domain: { id: domain.id }, id }
101
+ })
102
+
103
+ await onUpdateEmployeeAsSystemUser(patch, context)
104
+
105
+ const result = await repository.save({
106
+ ...employee,
107
+ ...patch,
108
+ supervisor,
109
+ department,
110
+ user: connectedUser,
111
+ updater: user
112
+ })
113
+
114
+ if (patch.photo) {
115
+ await deleteAttachmentsByRef(null, { refBys: [result.id] }, context)
116
+ await createAttachment(
117
+ null,
118
+ {
119
+ attachment: {
120
+ file: patch.photo,
121
+ refType: Employee.name,
122
+ refBy: result.id
123
+ }
124
+ },
125
+ context
126
+ )
127
+ }
128
+
129
+ return result
130
+ }
131
+
132
+ @Directive('@transaction')
133
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
134
+ @Mutation(returns => [Employee], {
135
+ description:
136
+ 'Updates or creates multiple employee records based on the provided patches. New employees are created if the "cuFlag" is "+", and existing employees are updated if the "cuFlag" is "M".'
137
+ })
138
+ async updateMultipleEmployee(
139
+ @Arg('patches', type => [EmployeePatch]) patches: EmployeePatch[],
140
+ @Ctx() context: ResolverContext
141
+ ): Promise<Employee[]> {
142
+ const { domain, user, tx } = context.state
143
+
144
+ let results = []
145
+ const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
146
+ const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
147
+ const employeeRepo = getRepository(Employee, tx)
148
+
149
+ if (_createRecords.length > 0) {
150
+ for (let i = 0; i < _createRecords.length; i++) {
151
+ const newRecord = _createRecords[i]
152
+
153
+ const supervisor =
154
+ 'supervisor' in newRecord
155
+ ? newRecord.supervisor && (await getRepository(Employee, tx).findOneBy({ id: newRecord.supervisor.id }))
156
+ : undefined
157
+
158
+ const department =
159
+ 'department' in newRecord
160
+ ? newRecord.department && (await getRepository(Department, tx).findOneBy({ id: newRecord.department.id }))
161
+ : undefined
162
+
163
+ const connectedUser =
164
+ 'user' in newRecord
165
+ ? newRecord.user && (await getRepository(User, tx).findOneBy({ id: newRecord.user.id }))
166
+ : undefined
167
+
168
+ const result = await employeeRepo.save({
169
+ ...newRecord,
170
+ supervisor,
171
+ department,
172
+ user: connectedUser,
173
+ domain,
174
+ creator: user,
175
+ updater: user
176
+ })
177
+
178
+ if (newRecord.photo) {
179
+ await createAttachment(
180
+ null,
181
+ {
182
+ attachment: {
183
+ file: newRecord.photo,
184
+ refType: Employee.name,
185
+ refBy: result.id
186
+ }
187
+ },
188
+ context
189
+ )
190
+ }
191
+
192
+ results.push({ ...result, cuFlag: '+' })
193
+ }
194
+ }
195
+
196
+ if (_updateRecords.length > 0) {
197
+ for (let i = 0; i < _updateRecords.length; i++) {
198
+ const updateRecord = _updateRecords[i]
199
+
200
+ const supervisor =
201
+ 'supervisor' in updateRecord
202
+ ? updateRecord.supervisor &&
203
+ (await getRepository(Employee, tx).findOneBy({ id: updateRecord.supervisor.id }))
204
+ : undefined
205
+
206
+ const department =
207
+ 'department' in updateRecord
208
+ ? updateRecord.department &&
209
+ (await getRepository(Department, tx).findOneBy({ id: updateRecord.department.id }))
210
+ : undefined
211
+
212
+ const connectedUser =
213
+ 'user' in updateRecord
214
+ ? updateRecord.user && (await getRepository(User, tx).findOneBy({ id: updateRecord.user.id }))
215
+ : undefined
216
+
217
+ const employee = await employeeRepo.findOneBy({ id: updateRecord.id })
218
+
219
+ await onUpdateEmployeeAsSystemUser(updateRecord, context)
220
+
221
+ const result = await employeeRepo.save({
222
+ ...employee,
223
+ ...updateRecord,
224
+ supervisor,
225
+ department,
226
+ user: connectedUser,
227
+ updater: user
228
+ })
229
+
230
+ if (updateRecord.photo) {
231
+ await deleteAttachmentsByRef(null, { refBys: [result.id] }, context)
232
+ await createAttachment(
233
+ null,
234
+ {
235
+ attachment: {
236
+ file: updateRecord.photo,
237
+ refType: Employee.name,
238
+ refBy: result.id
239
+ }
240
+ },
241
+ context
242
+ )
243
+ }
244
+
245
+ results.push({ ...result, cuFlag: 'M' })
246
+ }
247
+ }
248
+
249
+ return results
250
+ }
251
+
252
+ @Directive('@transaction')
253
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
254
+ @Mutation(returns => Boolean, {
255
+ description:
256
+ 'Deletes an employee record identified by the given ID. Also deletes any attachments associated with the employee.'
257
+ })
258
+ async deleteEmployee(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
259
+ const { domain, tx } = context.state
260
+
261
+ await onDeleteEmployeeAsSystemUser(id, context)
262
+ await getRepository(Employee, tx).delete({ domain: { id: domain.id }, id })
263
+ await deleteAttachmentsByRef(null, { refBys: [id] }, context)
264
+
265
+ return true
266
+ }
267
+
268
+ @Directive('@transaction')
269
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
270
+ @Mutation(returns => Boolean, {
271
+ description:
272
+ 'Deletes multiple employee records identified by the given IDs. Also deletes any attachments associated with each employee.'
273
+ })
274
+ async deleteEmployees(
275
+ @Arg('ids', type => [String]) ids: string[],
276
+ @Ctx() context: ResolverContext
277
+ ): Promise<boolean> {
278
+ const { domain, tx } = context.state
279
+
280
+ for (let id of ids) {
281
+ await await onDeleteEmployeeAsSystemUser(id, context)
282
+ }
283
+
284
+ await getRepository(Employee, tx).delete({
285
+ domain: { id: domain.id },
286
+ id: In(ids)
287
+ })
288
+
289
+ await deleteAttachmentsByRef(null, { refBys: ids }, context)
290
+
291
+ return true
292
+ }
293
+
294
+ @Directive('@transaction')
295
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
296
+ @Mutation(returns => Boolean, {
297
+ description:
298
+ 'Imports multiple employee records into the system. Each employee record must be provided in the EmployeePatch format.'
299
+ })
300
+ async importEmployees(
301
+ @Arg('employees', type => [EmployeePatch]) employees: EmployeePatch[],
302
+ @Ctx() context: ResolverContext
303
+ ): Promise<boolean> {
304
+ const { domain, tx } = context.state
305
+
306
+ await Promise.all(
307
+ employees.map(async (employee: EmployeePatch) => {
308
+ const createdEmployee: Employee = await getRepository(Employee, tx).save({ domain, ...employee })
309
+ })
310
+ )
311
+
312
+ return true
313
+ }
314
+
315
+ @Directive('@transaction')
316
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
317
+ @Mutation(returns => Employee, {
318
+ description:
319
+ 'Attaches an existing contact to an employee. The contact is identified by its ID and the employee is identified by their ID.'
320
+ })
321
+ async attachContact(
322
+ @Arg('id') id: string,
323
+ @Arg('contactId') contactId: string,
324
+ @Ctx() context: ResolverContext
325
+ ): Promise<Employee> {
326
+ const { domain, user, tx } = context.state
327
+
328
+ const employeeRepository = getRepository(Employee, tx)
329
+ const employee = await employeeRepository.findOne({
330
+ where: { domain: { id: domain.id }, id }
331
+ })
332
+
333
+ const contactRepository = getRepository(Contact, tx)
334
+ const contact = await contactRepository.findOne({
335
+ where: { domain: { id: domain.id }, id: contactId }
336
+ })
337
+
338
+ if (!contact) {
339
+ throw Error(`Contact not found with given contactId(${contactId}).`)
340
+ }
341
+
342
+ const result = await employeeRepository.save({
343
+ ...employee,
344
+ name: contact.name,
345
+ contact: { id: contactId },
346
+ updater: user
347
+ })
348
+
349
+ return result
350
+ }
351
+
352
+ @Directive('@transaction')
353
+ @Directive('@privilege(category: "employee", privilege: "mutation", domainOwnerGranted: true)')
354
+ @Mutation(returns => Employee, {
355
+ description: 'Detaches an existing contact from an employee. The employee is identified by their ID.'
356
+ })
357
+ async detachContact(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Employee> {
358
+ const { domain, user, tx } = context.state
359
+
360
+ const repository = getRepository(Employee, tx)
361
+ const employee = await repository.findOne({
362
+ where: { domain: { id: domain.id }, id }
363
+ })
364
+
365
+ const result = await repository.save({
366
+ ...employee,
367
+ contact: null,
368
+ updater: user
369
+ })
370
+
371
+ return result
372
+ }
373
+
374
+ @Directive('@transaction')
375
+ @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
376
+ @Mutation(returns => Boolean, {
377
+ description:
378
+ 'Registers an existing employee as a system user, granting them access to the system. The employee is identified by their ID.'
379
+ })
380
+ async registerEmployeeAsSystemUser(
381
+ @Arg('employeeId', { description: 'Employee Id' }) employeeId: string,
382
+ @Ctx() context: ResolverContext
383
+ ): Promise<boolean> {
384
+ return !!(await registerEmployeeAsSystemUser(employeeId, context))
385
+ }
386
+ }