@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.
- package/client/bootstrap.ts +23 -0
- package/client/component/approval-line-brief.ts +119 -0
- package/client/component/approval-line-items-editor-popup.ts +91 -0
- package/client/component/approval-line-items-editor.ts +325 -0
- package/client/component/approval-line-selector.ts +235 -0
- package/client/component/approval-line-templates-manager.ts +229 -0
- package/client/component/approval-line-view.ts +122 -0
- package/client/component/assignees-editor-popup.ts +79 -0
- package/client/component/assignees-editor.ts +217 -0
- package/client/component/assignees-view.ts +55 -0
- package/client/component/department-selector.ts +151 -0
- package/client/component/department-view.ts +107 -0
- package/client/component/index.ts +16 -0
- package/client/component/recipients-editor-popup.ts +79 -0
- package/client/component/recipients-editor.ts +212 -0
- package/client/component/recipients-view.ts +55 -0
- package/client/grist-editor/grist-editor-approval-line.ts +70 -0
- package/client/grist-editor/grist-editor-assignees.ts +69 -0
- package/client/grist-editor/grist-editor-department-object.ts +78 -0
- package/client/grist-editor/grist-editor-recipients.ts +69 -0
- package/client/grist-editor/grist-renderer-approval-line.ts +13 -0
- package/client/grist-editor/grist-renderer-assignees.ts +13 -0
- package/client/grist-editor/grist-renderer-department-object.ts +13 -0
- package/client/grist-editor/grist-renderer-recipients.ts +13 -0
- package/client/index.ts +2 -0
- package/client/pages/approval-line/common-approval-line-templates-page.ts +382 -0
- package/client/pages/approval-line/my-approval-line-templates-page.ts +385 -0
- package/client/pages/department/department-importer.ts +87 -0
- package/client/pages/department/department-list-page.ts +450 -0
- package/client/pages/department/department-tree-page.ts +379 -0
- package/client/pages/employee/employee-importer.ts +87 -0
- package/client/pages/employee/employee-list-page.ts +772 -0
- package/client/pages/employee/employees-by-department.ts +519 -0
- package/client/route.ts +27 -0
- package/client/tsconfig.json +13 -0
- package/client/types/approval-line.ts +52 -0
- package/client/types/contact.ts +51 -0
- package/client/types/department.ts +29 -0
- package/client/types/employee.ts +50 -0
- package/client/types/index.ts +5 -0
- package/client/types/org-member.ts +27 -0
- package/dist-client/bootstrap.js +1 -8
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/pages/employee/employee-list-page.js +3 -3
- package/dist-client/pages/employee/employee-list-page.js.map +1 -1
- package/dist-client/pages/employee/employees-by-department.js +2 -2
- package/dist-client/pages/employee/employees-by-department.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/employee/employee-history.d.ts +2 -6
- package/dist-server/service/employee/employee-history.js +3 -23
- package/dist-server/service/employee/employee-history.js.map +1 -1
- package/dist-server/service/employee/employee-query.js +1 -1
- package/dist-server/service/employee/employee-query.js.map +1 -1
- package/dist-server/service/employee/employee-type.d.ts +5 -13
- package/dist-server/service/employee/employee-type.js +7 -39
- package/dist-server/service/employee/employee-type.js.map +1 -1
- package/dist-server/service/employee/employee.d.ts +2 -6
- package/dist-server/service/employee/employee.js +3 -23
- package/dist-server/service/employee/employee.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/server/controllers/register-employee-as-system-user.ts +136 -0
- package/server/index.ts +3 -0
- package/server/migrations/1723861013111-seed-organization-codes.ts +127 -0
- package/server/migrations/index.ts +9 -0
- package/server/routes.ts +26 -0
- package/server/service/approval-line/approval-line-item.ts +42 -0
- package/server/service/approval-line/approval-line-mutation.ts +394 -0
- package/server/service/approval-line/approval-line-query.ts +208 -0
- package/server/service/approval-line/approval-line-type.ts +63 -0
- package/server/service/approval-line/approval-line.ts +123 -0
- package/server/service/approval-line/index.ts +7 -0
- package/server/service/department/department-history.ts +141 -0
- package/server/service/department/department-mutation.ts +231 -0
- package/server/service/department/department-query.ts +131 -0
- package/server/service/department/department-type.ts +74 -0
- package/server/service/department/department.ts +116 -0
- package/server/service/department/event-subscriber.ts +17 -0
- package/server/service/department/index.ts +9 -0
- package/server/service/employee/employee-history.ts +173 -0
- package/server/service/employee/employee-mutation.ts +386 -0
- package/server/service/employee/employee-query.ts +172 -0
- package/server/service/employee/employee-type.ts +176 -0
- package/server/service/employee/employee.ts +177 -0
- package/server/service/employee/event-subscriber.ts +17 -0
- package/server/service/employee/index.ts +9 -0
- package/server/service/index.ts +39 -0
- package/server/tsconfig.json +10 -0
- package/dist-client/filters-form/filter-department-object.d.ts +0 -3
- package/dist-client/filters-form/filter-department-object.js +0 -8
- package/dist-client/filters-form/filter-department-object.js.map +0 -1
- package/dist-client/filters-form/ox-filter-department-object.d.ts +0 -15
- package/dist-client/filters-form/ox-filter-department-object.js +0 -130
- package/dist-client/filters-form/ox-filter-department-object.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/organization",
|
|
3
|
-
"version": "8.0.0
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@operato/app": "^8.0.0
|
|
31
|
-
"@operato/contact": "^8.0.0
|
|
32
|
-
"@operato/data-tree": "^8.0.0
|
|
33
|
-
"@operato/graphql": "^8.0.0
|
|
34
|
-
"@operato/p13n": "^8.0.0
|
|
35
|
-
"@operato/shell": "^8.0.0
|
|
36
|
-
"@things-factory/auth-base": "^8.0.0
|
|
37
|
-
"@things-factory/code-ui": "^8.0.0
|
|
38
|
-
"@things-factory/contact": "^8.0.0
|
|
39
|
-
"@things-factory/shell": "^8.0.0
|
|
30
|
+
"@operato/app": "^8.0.0",
|
|
31
|
+
"@operato/contact": "^8.0.0",
|
|
32
|
+
"@operato/data-tree": "^8.0.0",
|
|
33
|
+
"@operato/graphql": "^8.0.0",
|
|
34
|
+
"@operato/p13n": "^8.0.0",
|
|
35
|
+
"@operato/shell": "^8.0.0",
|
|
36
|
+
"@things-factory/auth-base": "^8.0.0",
|
|
37
|
+
"@things-factory/code-ui": "^8.0.0",
|
|
38
|
+
"@things-factory/contact": "^8.0.0",
|
|
39
|
+
"@things-factory/shell": "^8.0.0"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "07ef27d272dd9a067a9648ac7013748510556a18"
|
|
42
42
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { getRepository } from '@things-factory/shell'
|
|
2
|
+
import { Employee } from '../service/employee/employee'
|
|
3
|
+
import { registerContactAsSystemUser } from '@things-factory/contact'
|
|
4
|
+
import { Role, User } from '@things-factory/auth-base'
|
|
5
|
+
import { EmployeePatch } from 'service/employee/employee-type'
|
|
6
|
+
|
|
7
|
+
export async function registerEmployeeAsSystemUser(employeeId: string, context: ResolverContext) {
|
|
8
|
+
const { domain, tx } = context.state
|
|
9
|
+
|
|
10
|
+
const employeeRepository = getRepository(Employee, tx)
|
|
11
|
+
|
|
12
|
+
const employee = await employeeRepository.findOne({
|
|
13
|
+
where: {
|
|
14
|
+
id: employeeId,
|
|
15
|
+
domain: { id: domain.id }
|
|
16
|
+
},
|
|
17
|
+
relations: ['contact', 'user']
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (employee.user) {
|
|
21
|
+
throw new Error(context.t('error.employee-already-registered'))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!employee.contact) {
|
|
25
|
+
throw new Error(context.t('error.contact-information-required'))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const systemUser = await registerContactAsSystemUser(
|
|
29
|
+
{
|
|
30
|
+
contactId: employee.contact.id,
|
|
31
|
+
roleName: employee.active ? employee.jobResponsibility : ''
|
|
32
|
+
},
|
|
33
|
+
context
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
employee.user = systemUser
|
|
37
|
+
|
|
38
|
+
await employeeRepository.save(employee)
|
|
39
|
+
|
|
40
|
+
return systemUser
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function onUpdateEmployeeAsSystemUser(patch: EmployeePatch, context: ResolverContext) {
|
|
44
|
+
const { domain, user, tx } = context.state
|
|
45
|
+
const { id, jobResponsibility } = patch
|
|
46
|
+
|
|
47
|
+
const employeeRepository = getRepository(Employee, tx)
|
|
48
|
+
|
|
49
|
+
const employee = await employeeRepository.findOne({
|
|
50
|
+
where: {
|
|
51
|
+
id,
|
|
52
|
+
domain: { id: domain.id }
|
|
53
|
+
},
|
|
54
|
+
relations: ['contact', 'user']
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (!employee.user) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { jobResponsibility: lastJobResponsibility } = employee
|
|
62
|
+
|
|
63
|
+
if ('jobResponsibility' in patch && lastJobResponsibility !== jobResponsibility) {
|
|
64
|
+
const userRepository = getRepository(User, tx)
|
|
65
|
+
|
|
66
|
+
const systemUser = await userRepository.findOne({
|
|
67
|
+
where: { id: employee.user.id },
|
|
68
|
+
relations: ['domains', 'roles']
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const { roles } = systemUser
|
|
72
|
+
let newRoles = [...roles]
|
|
73
|
+
|
|
74
|
+
if (lastJobResponsibility) {
|
|
75
|
+
const lastRole = await getRepository(Role, tx).findOne({
|
|
76
|
+
where: { name: lastJobResponsibility, domain: { id: domain.id } }
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if (lastRole) {
|
|
80
|
+
newRoles = roles.filter(role => role.id !== lastRole.id)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const newRole = await getRepository(Role, tx).findOne({
|
|
85
|
+
where: { name: jobResponsibility, domain: { id: domain.id } }
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
if (newRole && !roles.find(role => role.id === newRole.id)) {
|
|
89
|
+
newRoles = [...newRoles, newRole]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
systemUser.roles = newRoles
|
|
93
|
+
systemUser.updater = user
|
|
94
|
+
|
|
95
|
+
await userRepository.save(systemUser)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function onDeleteEmployeeAsSystemUser(employeeId: string, context: ResolverContext) {
|
|
100
|
+
const { domain, user, tx } = context.state
|
|
101
|
+
|
|
102
|
+
const employeeRepository = getRepository(Employee, tx)
|
|
103
|
+
|
|
104
|
+
const employee = await employeeRepository.findOne({
|
|
105
|
+
where: {
|
|
106
|
+
id: employeeId,
|
|
107
|
+
domain: { id: domain.id }
|
|
108
|
+
},
|
|
109
|
+
relations: ['user']
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
if (!employee.user) {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { jobResponsibility } = employee
|
|
117
|
+
|
|
118
|
+
if (jobResponsibility) {
|
|
119
|
+
const userRepository = getRepository(User, tx)
|
|
120
|
+
|
|
121
|
+
const systemUser = await userRepository.findOne({
|
|
122
|
+
where: { id: employee.user.id },
|
|
123
|
+
relations: ['domains', 'roles']
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const lastRole = await getRepository(Role, tx).findOne({
|
|
127
|
+
where: { name: jobResponsibility, domain: { id: domain.id } }
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (lastRole) {
|
|
131
|
+
systemUser.roles = systemUser.roles.filter(role => role.id !== lastRole.id)
|
|
132
|
+
systemUser.updater = user
|
|
133
|
+
await userRepository.save(systemUser)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
package/server/index.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
import { logger } from '@things-factory/env'
|
|
4
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
|
5
|
+
import { User } from '@things-factory/auth-base'
|
|
6
|
+
import { CommonCode, CommonCodeDetail } from '@things-factory/code-base'
|
|
7
|
+
|
|
8
|
+
const SEED_COMMON_CODES = [
|
|
9
|
+
{
|
|
10
|
+
name: 'EMPLOYEE_TYPE',
|
|
11
|
+
description: '직원 유형',
|
|
12
|
+
details: [
|
|
13
|
+
{
|
|
14
|
+
name: 'FULLTIME',
|
|
15
|
+
description: '풀타임 정규직',
|
|
16
|
+
labels: null,
|
|
17
|
+
rank: 1
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'PARTTIME',
|
|
21
|
+
description: '파트타임 정규직',
|
|
22
|
+
labels: null,
|
|
23
|
+
rank: 2
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'TEMPORARY',
|
|
27
|
+
description: '임시직',
|
|
28
|
+
labels: null,
|
|
29
|
+
rank: 3
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'JOB_POSITION',
|
|
35
|
+
description: '직급',
|
|
36
|
+
details: [
|
|
37
|
+
// {
|
|
38
|
+
// name: '임원',
|
|
39
|
+
// description: '임원',
|
|
40
|
+
// labels: null,
|
|
41
|
+
// rank: 1
|
|
42
|
+
// },
|
|
43
|
+
// {
|
|
44
|
+
// name: '직원',
|
|
45
|
+
// description: '직원',
|
|
46
|
+
// labels: null,
|
|
47
|
+
// rank: 2
|
|
48
|
+
// }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'JOB_RESPONSIBILITY',
|
|
53
|
+
description: '직책',
|
|
54
|
+
details: [
|
|
55
|
+
// {
|
|
56
|
+
// name: '상담사',
|
|
57
|
+
// description: '상담사',
|
|
58
|
+
// labels: null,
|
|
59
|
+
// rank: 1
|
|
60
|
+
// },
|
|
61
|
+
// {
|
|
62
|
+
// name: '매니저',
|
|
63
|
+
// description: '매니저',
|
|
64
|
+
// labels: null,
|
|
65
|
+
// rank: 2
|
|
66
|
+
// }
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
export class SeedOrganizationCodes1723861013111 implements MigrationInterface {
|
|
72
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
73
|
+
const commonCodeRepository = getRepository(CommonCode)
|
|
74
|
+
const commonCodeDetailRepository = getRepository(CommonCodeDetail)
|
|
75
|
+
const domainRepository = getRepository(Domain)
|
|
76
|
+
const userRepository = getRepository(User)
|
|
77
|
+
|
|
78
|
+
const domain: Domain = await domainRepository.findOne({
|
|
79
|
+
where: { name: 'SYSTEM' }
|
|
80
|
+
})
|
|
81
|
+
const user = await userRepository.findOne({ where: { id: domain.owner } })
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
for (let i = 0; i < SEED_COMMON_CODES.length; i++) {
|
|
85
|
+
const { name, description, details } = SEED_COMMON_CODES[i]
|
|
86
|
+
|
|
87
|
+
const commonCode = await commonCodeRepository.save({
|
|
88
|
+
domain,
|
|
89
|
+
name,
|
|
90
|
+
description,
|
|
91
|
+
creator: user,
|
|
92
|
+
updater: user
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
for (const commonCodeDetail of details) {
|
|
96
|
+
const { name, description, labels, rank } = commonCodeDetail
|
|
97
|
+
|
|
98
|
+
await commonCodeDetailRepository.save({
|
|
99
|
+
domain,
|
|
100
|
+
commonCode,
|
|
101
|
+
name,
|
|
102
|
+
description,
|
|
103
|
+
labels,
|
|
104
|
+
rank
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
logger.error(e)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
114
|
+
const domainRepository = getRepository(Domain)
|
|
115
|
+
|
|
116
|
+
const domain: Domain = await domainRepository.findOne({
|
|
117
|
+
where: { name: 'SYSTEM' }
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const repository = getRepository(CommonCode)
|
|
121
|
+
|
|
122
|
+
SEED_COMMON_CODES.reverse().forEach(async commonCode => {
|
|
123
|
+
let record = await repository.findOne({ where: { name: commonCode.name, domain: { id: domain.id } } })
|
|
124
|
+
record && (await repository.remove(record))
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const glob = require('glob')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
export var migrations = []
|
|
5
|
+
|
|
6
|
+
glob.sync(path.resolve(__dirname, '.', '**', '*.js')).forEach(function(file) {
|
|
7
|
+
if (file.indexOf('index.js') !== -1) return
|
|
8
|
+
migrations = migrations.concat(Object.values(require(path.resolve(file))) || [])
|
|
9
|
+
})
|
package/server/routes.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
|
|
2
|
+
/*
|
|
3
|
+
* can add global public routes to application (auth not required, tenancy not required)
|
|
4
|
+
*
|
|
5
|
+
* ex) routes.get('/path', async(context, next) => {})
|
|
6
|
+
* ex) routes.post('/path', async(context, next) => {})
|
|
7
|
+
*/
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
|
|
11
|
+
/*
|
|
12
|
+
* can add global private routes to application (auth required, tenancy not required)
|
|
13
|
+
*/
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
|
|
17
|
+
/*
|
|
18
|
+
* can add domain public routes to application (auth not required, tenancy required)
|
|
19
|
+
*/
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
|
|
23
|
+
/*
|
|
24
|
+
* can add domain private routes to application (auth required, tenancy required)
|
|
25
|
+
*/
|
|
26
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ObjectType, Field, ID, registerEnumType } from 'type-graphql'
|
|
2
|
+
|
|
3
|
+
export enum OrgMemberTargetType {
|
|
4
|
+
Employee = 'Employee',
|
|
5
|
+
Department = 'Department',
|
|
6
|
+
Role = 'Role',
|
|
7
|
+
Myself = 'Myself',
|
|
8
|
+
MyDepartment = 'MyDepartment',
|
|
9
|
+
MySupervisor = 'MySupervisor'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
registerEnumType(OrgMemberTargetType, {
|
|
13
|
+
name: 'OrgMemberTargetType',
|
|
14
|
+
description: 'type enumeration of a approval line item'
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
@ObjectType()
|
|
18
|
+
export class OrgMemberTarget {
|
|
19
|
+
@Field(() => ID, { description: 'Field id' })
|
|
20
|
+
id?: string
|
|
21
|
+
|
|
22
|
+
@Field({ nullable: true, description: 'Field name' })
|
|
23
|
+
name?: string
|
|
24
|
+
|
|
25
|
+
@Field({ nullable: true, description: 'Field description' })
|
|
26
|
+
description?: string
|
|
27
|
+
|
|
28
|
+
@Field({ nullable: true })
|
|
29
|
+
controlNo?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@ObjectType({ description: 'Entity for approval line item' })
|
|
33
|
+
export class ApprovalLineItem {
|
|
34
|
+
@Field({ nullable: true })
|
|
35
|
+
type?: OrgMemberTargetType
|
|
36
|
+
|
|
37
|
+
@Field(type => String, { nullable: true })
|
|
38
|
+
value?: string
|
|
39
|
+
|
|
40
|
+
@Field(type => OrgMemberTarget, { nullable: true })
|
|
41
|
+
approver?: OrgMemberTarget
|
|
42
|
+
}
|