@things-factory/organization 10.1.87 → 10.1.104
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/dist-server/ai-entry/entry-target-shape.d.ts +82 -0
- package/dist-server/ai-entry/entry-target-shape.js +3 -0
- package/dist-server/ai-entry/entry-target-shape.js.map +1 -0
- package/dist-server/ai-entry/organization-entry-rules.d.ts +56 -0
- package/dist-server/ai-entry/organization-entry-rules.js +130 -0
- package/dist-server/ai-entry/organization-entry-rules.js.map +1 -0
- package/dist-server/ai-entry/organization-entry-targets.d.ts +12 -0
- package/dist-server/ai-entry/organization-entry-targets.js +229 -0
- package/dist-server/ai-entry/organization-entry-targets.js.map +1 -0
- package/dist-server/index.d.ts +2 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/tests/organization-entry-targets.test.ts +236 -0
- package/translations/en.json +14 -0
- package/translations/ja.json +14 -0
- package/translations/ko.json +14 -0
- package/translations/ms.json +14 -0
- package/translations/zh.json +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/organization",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.104",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"@operato/popup": "^10.15.0",
|
|
38
38
|
"@operato/shell": "^10.0.0",
|
|
39
39
|
"@things-factory/auth-base": "^10.1.79",
|
|
40
|
-
"@things-factory/code-ui": "^10.1.
|
|
40
|
+
"@things-factory/code-ui": "^10.1.104",
|
|
41
41
|
"@things-factory/contact": "^10.1.79",
|
|
42
42
|
"@things-factory/shell": "^10.1.79"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "0d05c7573220c6da2dec2654a4b86d7bd8e3c4fb"
|
|
45
45
|
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The organization's AI 입력 declarations - the department, and the employee no application opens yet.
|
|
3
|
+
*
|
|
4
|
+
* The assistant's own validation (declareEntryTarget) is asked here through its source file, which needs only
|
|
5
|
+
* `graphql`: the package itself keeps no run-time dependency on the assistant.
|
|
6
|
+
*/
|
|
7
|
+
import { parse } from 'graphql'
|
|
8
|
+
|
|
9
|
+
import { clearEntryTargets, declareEntryTarget } from '../../ai-assistant/server/service/data-entry/entry-target'
|
|
10
|
+
import { checkedParent } from '../server/service/department/department-parent'
|
|
11
|
+
import { departmentChangeIssues, departmentIssues, employeeIssues, parentVerdictOf } from '../server/ai-entry/organization-entry-rules'
|
|
12
|
+
import {
|
|
13
|
+
EMPLOYEE_FORBIDDEN_FIELDS,
|
|
14
|
+
departmentEntryTarget,
|
|
15
|
+
employeeEntryTarget,
|
|
16
|
+
organizationEntryTargets
|
|
17
|
+
} from '../server/ai-entry/organization-entry-targets'
|
|
18
|
+
|
|
19
|
+
import en from '../translations/en.json'
|
|
20
|
+
import ja from '../translations/ja.json'
|
|
21
|
+
import ko from '../translations/ko.json'
|
|
22
|
+
import ms from '../translations/ms.json'
|
|
23
|
+
import zh from '../translations/zh.json'
|
|
24
|
+
|
|
25
|
+
const LANGUAGES: Record<string, Record<string, string>> = { en, ja, ko, ms, zh }
|
|
26
|
+
|
|
27
|
+
const rootFields = (document: string) => {
|
|
28
|
+
const operations = parse(document).definitions.filter((one: any) => one.kind === 'OperationDefinition') as any[]
|
|
29
|
+
expect(operations).toHaveLength(1)
|
|
30
|
+
return operations[0].selectionSet.selections.map((one: any) => one.name.value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const row = (values: Record<string, unknown>, index = 0) => ({ index, values, sources: [], issues: [] })
|
|
34
|
+
|
|
35
|
+
describe('organization entry targets', () => {
|
|
36
|
+
afterEach(() => clearEntryTargets())
|
|
37
|
+
|
|
38
|
+
it('are accepted by the assistant as they are declared', () => {
|
|
39
|
+
for (const target of [...organizationEntryTargets(), employeeEntryTarget()]) expect(() => declareEntryTarget(target as any)).not.toThrow()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('registering the list as a whole never brings the employee along - it is taken only by name', () => {
|
|
43
|
+
expect(organizationEntryTargets().map(target => target.key)).toEqual(['organization.department'])
|
|
44
|
+
expect(employeeEntryTarget().key).toBe('organization.employee')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('every document has one root field - the door the person must pass', () => {
|
|
48
|
+
const department = departmentEntryTarget()
|
|
49
|
+
expect(rootFields(department.mutation)).toEqual(['createDepartment'])
|
|
50
|
+
expect(rootFields(department.update!.mutation)).toEqual(['updateDepartment'])
|
|
51
|
+
expect(rootFields(department.update!.read.query)).toEqual(['departments'])
|
|
52
|
+
expect(rootFields(department.update!.find!.query)).toEqual(['departments'])
|
|
53
|
+
expect(rootFields(department.fields.find(field => field.name === 'parent')!.reference!.query)).toEqual(['departments'])
|
|
54
|
+
|
|
55
|
+
const employee = employeeEntryTarget()
|
|
56
|
+
expect(rootFields(employee.mutation)).toEqual(['createEmployee'])
|
|
57
|
+
expect(rootFields(employee.fields.find(field => field.name === 'department')!.reference!.query)).toEqual(['departments'])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('a department is created with the declared fields only, and its parent as an id', () => {
|
|
61
|
+
const variables = departmentEntryTarget().variables(
|
|
62
|
+
row({ controlNo: 'D10', name: '영업1팀', parent: { id: 'p-1', label: 'D1 · 영업본부' }, manager: { id: 'e-1' }, picture: 'x' })
|
|
63
|
+
)
|
|
64
|
+
expect(variables).toEqual({ department: { controlNo: 'D10', name: '영업1팀', parent: { id: 'p-1' } } })
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('a department patch carries only the fields the update declares - never manager, picture or extension', () => {
|
|
68
|
+
const update = departmentEntryTarget().update!
|
|
69
|
+
expect(update.fields).toEqual(['controlNo', 'name', 'description', 'active', 'parent'])
|
|
70
|
+
const { patch } = update.variables({
|
|
71
|
+
id: 'd-1',
|
|
72
|
+
record: { id: 'd-1' },
|
|
73
|
+
values: { name: '영업2팀', parent: { id: 'p-2' }, manager: { id: 'e-1' }, picture: 'x', extension: '1234', cuFlag: 'M' }
|
|
74
|
+
}) as any
|
|
75
|
+
expect(patch).toEqual({ name: '영업2팀', parent: { id: 'p-2' } })
|
|
76
|
+
expect(Object.keys(patch).every(name => update.fields!.includes(name))).toBe(true)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('a cleared parent goes as null - the department becomes top-level', () => {
|
|
80
|
+
const { patch } = departmentEntryTarget().update!.variables({ id: 'd-1', record: { id: 'd-1' }, values: { parent: null } }) as any
|
|
81
|
+
expect(patch).toEqual({ parent: null })
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('the parent reference finds a department by its exact name or 관리번호, never a piece of it', () => {
|
|
85
|
+
const reference = departmentEntryTarget().fields.find(field => field.name === 'parent')!.reference!
|
|
86
|
+
const data = { departments: { items: [{ id: 'a', name: '영업본부', controlNo: 'D1' }, { id: 'b', name: '영업본부 지원', controlNo: 'D2' }] } }
|
|
87
|
+
expect(reference.recordsOf(data, '영업본부').map(one => one.id)).toEqual(['a'])
|
|
88
|
+
expect(reference.recordsOf(data, 'd2').map(one => one.id)).toEqual(['b'])
|
|
89
|
+
expect(reference.recordsOf(data, '영업')).toEqual([])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('department checks', () => {
|
|
94
|
+
it('a name or a 관리번호 the domain already has keeps the row out', () => {
|
|
95
|
+
const issues = departmentIssues([row({ controlNo: 'D9', name: '총무팀' }), row({ controlNo: 'D1', name: '새팀' }), row({ controlNo: 'D8', name: '새팀2' })], [
|
|
96
|
+
{ name: '총무팀', controlNo: 'D3' },
|
|
97
|
+
{ name: '기획팀', controlNo: 'D1' }
|
|
98
|
+
])
|
|
99
|
+
expect(issues[0]).toEqual([expect.objectContaining({ field: 'name', code: 'duplicate', blocking: true })])
|
|
100
|
+
expect(issues[1]).toEqual([expect.objectContaining({ field: 'controlNo', code: 'duplicate', blocking: true })])
|
|
101
|
+
expect(issues[2]).toEqual([])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('a row repeating an earlier row of the proposal is kept out - the later one', () => {
|
|
105
|
+
const issues = departmentIssues([row({ controlNo: 'D1', name: 'A' }), row({ controlNo: 'D2', name: 'A' }), row({ controlNo: 'D1', name: 'B' })], [])
|
|
106
|
+
expect(issues[0]).toEqual([])
|
|
107
|
+
expect(issues[1]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
|
|
108
|
+
expect(issues[2]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('a parent named by another row of the proposal keeps the row out until the parent is entered', () => {
|
|
112
|
+
const issues = departmentIssues(
|
|
113
|
+
[
|
|
114
|
+
row({ controlNo: 'D11', name: '영업1팀', parent: { text: '영업본부' } }),
|
|
115
|
+
row({ controlNo: 'D1', name: '영업본부' }),
|
|
116
|
+
row({ controlNo: 'D12', name: '영업2팀', parent: { text: 'D1' } }),
|
|
117
|
+
row({ controlNo: 'D13', name: '영업3팀', parent: { id: 'p-1' } })
|
|
118
|
+
],
|
|
119
|
+
[]
|
|
120
|
+
)
|
|
121
|
+
expect(issues[0]).toEqual([
|
|
122
|
+
expect.objectContaining({ field: 'parent', code: 'parent-in-proposal', messageKey: 'text.organization-entry-department-parent-first', params: { parent: '영업본부' }, blocking: true })
|
|
123
|
+
])
|
|
124
|
+
expect(issues[1]).toEqual([])
|
|
125
|
+
expect(issues[2]).toEqual([expect.objectContaining({ code: 'parent-in-proposal', blocking: true })])
|
|
126
|
+
expect(issues[3]).toEqual([])
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('department change checks', () => {
|
|
131
|
+
const existing = [
|
|
132
|
+
{ id: 'a', name: '영업팀', controlNo: 'D1' },
|
|
133
|
+
{ id: 'b', name: '총무팀', controlNo: 'D2' }
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
it('a new name or 관리번호 another department has keeps the change out; the record itself does not', () => {
|
|
137
|
+
const issues = departmentChangeIssues(
|
|
138
|
+
[
|
|
139
|
+
{ id: 'a', values: { name: '총무팀' } },
|
|
140
|
+
{ id: 'a', values: { controlNo: 'D2' } },
|
|
141
|
+
{ id: 'a', values: { name: '영업팀', controlNo: 'D1' } },
|
|
142
|
+
{ id: 'c', values: { description: 'x' } }
|
|
143
|
+
],
|
|
144
|
+
existing
|
|
145
|
+
)
|
|
146
|
+
expect(issues[0]).toEqual([expect.objectContaining({ field: 'name', code: 'duplicate', blocking: true })])
|
|
147
|
+
expect(issues[1]).toEqual([expect.objectContaining({ field: 'controlNo', code: 'duplicate', blocking: true })])
|
|
148
|
+
expect(issues[2]).toEqual([])
|
|
149
|
+
expect(issues[3]).toEqual([])
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('two changes giving the same new name - the later one is kept out', () => {
|
|
153
|
+
const issues = departmentChangeIssues([{ id: 'a', values: { name: '새팀' } }, { id: 'b', values: { name: '새팀' } }], existing)
|
|
154
|
+
expect(issues[0]).toEqual([])
|
|
155
|
+
expect(issues[1]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('a parent the department rule refuses keeps the change out', () => {
|
|
159
|
+
const issues = departmentChangeIssues([{ id: 'a', values: { parent: { id: 'a' } } }, { id: 'b', values: { parent: { id: 'z' } } }, { id: 'c', values: { parent: { id: 'a' } } }], [], [
|
|
160
|
+
'loop',
|
|
161
|
+
'not-found',
|
|
162
|
+
undefined
|
|
163
|
+
])
|
|
164
|
+
expect(issues[0]).toEqual([expect.objectContaining({ field: 'parent', code: 'parent-loop', blocking: true })])
|
|
165
|
+
expect(issues[1]).toEqual([expect.objectContaining({ field: 'parent', code: 'parent-not-found', blocking: true })])
|
|
166
|
+
expect(issues[2]).toEqual([])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it("the verdict is read from checkedParent itself - itself, its own sub-department, an unseen one, a fine one", async () => {
|
|
170
|
+
/* 본부(hq) ─ 1팀(t1) ─ 1파트(p1); 2팀(t2) under 본부 */
|
|
171
|
+
const rows: Record<string, { id: string; parentId?: string }> = { hq: { id: 'hq' }, t1: { id: 't1', parentId: 'hq' }, p1: { id: 'p1', parentId: 't1' }, t2: { id: 't2', parentId: 'hq' } }
|
|
172
|
+
const repository = { findOne: async ({ where }: any) => rows[where.id] ?? null } as any
|
|
173
|
+
const verdict = async (id: string, parent: string) => {
|
|
174
|
+
try {
|
|
175
|
+
await checkedParent(id, { parent: { id: parent } }, repository, { id: 'domain' } as any)
|
|
176
|
+
return undefined
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return parentVerdictOf(error)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
expect(await verdict('t1', 't1')).toBe('loop')
|
|
182
|
+
expect(await verdict('t1', 'p1')).toBe('loop')
|
|
183
|
+
expect(await verdict('t1', 'nowhere')).toBe('not-found')
|
|
184
|
+
expect(await verdict('p1', 't2')).toBeUndefined()
|
|
185
|
+
expect(parentVerdictOf(new Error('connection lost'))).toBeUndefined()
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('employee target - declared, opened by no application', () => {
|
|
190
|
+
it('declares none of the fields that must not go to a model', () => {
|
|
191
|
+
const names = employeeEntryTarget().fields.map(field => field.name)
|
|
192
|
+
expect(names).toEqual(['controlNo', 'name', 'jobPosition', 'jobResponsibility', 'type', 'hiredOn', 'department'])
|
|
193
|
+
for (const forbidden of EMPLOYEE_FORBIDDEN_FIELDS) expect(names).not.toContain(forbidden)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('does not pass a forbidden field even when a row carries one, and has no update', () => {
|
|
197
|
+
const target = employeeEntryTarget()
|
|
198
|
+
const variables = target.variables(
|
|
199
|
+
row({ controlNo: 'E1', name: '홍길동', department: { id: 'd-1' }, bankAccount: '1', supervisor: { id: 's' }, user: { id: 'u' }, photo: 'x' })
|
|
200
|
+
)
|
|
201
|
+
expect(variables).toEqual({ employee: { controlNo: 'E1', name: '홍길동', department: { id: 'd-1' } } })
|
|
202
|
+
expect(target.update).toBeUndefined()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('a 관리번호 the domain already has, or one repeated in the proposal, keeps the row out', () => {
|
|
206
|
+
const issues = employeeIssues([row({ controlNo: 'E1' }), row({ controlNo: 'E2' }), row({ controlNo: 'E2' })], [{ controlNo: 'E1' }])
|
|
207
|
+
expect(issues[0]).toEqual([expect.objectContaining({ code: 'duplicate', blocking: true })])
|
|
208
|
+
expect(issues[1]).toEqual([])
|
|
209
|
+
expect(issues[2]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe('translations', () => {
|
|
214
|
+
it('the five languages have the same keys', () => {
|
|
215
|
+
const reference = Object.keys(en).sort()
|
|
216
|
+
for (const [language, table] of Object.entries(LANGUAGES)) expect({ language, keys: Object.keys(table).sort() }).toEqual({ language, keys: reference })
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('every label and message key the targets name is translated in every language', () => {
|
|
220
|
+
const departmentRows = [row({ controlNo: 'D1', name: 'A', parent: { text: 'B' } }), row({ controlNo: 'D1', name: 'B' })]
|
|
221
|
+
const keys = [
|
|
222
|
+
...[...organizationEntryTargets(), employeeEntryTarget()].map(target => target.labelKey),
|
|
223
|
+
'text.organization-entry-department-parent-loop',
|
|
224
|
+
'text.organization-entry-department-parent-not-found',
|
|
225
|
+
...departmentIssues(departmentRows, [{ name: 'A', controlNo: 'D1' }]).flat().map(issue => issue.messageKey!),
|
|
226
|
+
...departmentIssues(departmentRows, []).flat().map(issue => issue.messageKey!),
|
|
227
|
+
...employeeIssues([row({ controlNo: 'E1' }), row({ controlNo: 'E2' }), row({ controlNo: 'E2' })], [{ controlNo: 'E1' }]).flat().map(issue => issue.messageKey!),
|
|
228
|
+
'field.extension',
|
|
229
|
+
'text.employee-type-fulltime',
|
|
230
|
+
'text.employee-type-parttime',
|
|
231
|
+
'text.employee-type-temporary'
|
|
232
|
+
]
|
|
233
|
+
expect(new Set(keys).size).toBeGreaterThanOrEqual(10)
|
|
234
|
+
for (const key of keys) for (const [language, table] of Object.entries(LANGUAGES)) expect({ language, key, has: key in table }).toEqual({ language, key, has: true })
|
|
235
|
+
})
|
|
236
|
+
})
|
package/translations/en.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"field.approval-line": "approval line",
|
|
15
15
|
"field.approver": "approver",
|
|
16
16
|
"field.control-no": "control No.",
|
|
17
|
+
"field.extension": "extension",
|
|
17
18
|
"field.hired-on": "hired on",
|
|
18
19
|
"field.note": "note",
|
|
19
20
|
"field.notify-after": "notify at",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"label.manager": "manager",
|
|
44
45
|
"label.myself": "myself",
|
|
45
46
|
"label.note": "note",
|
|
47
|
+
"label.organization-entry-department": "department",
|
|
48
|
+
"label.organization-entry-employee": "employee",
|
|
46
49
|
"label.participation-approval": "approval",
|
|
47
50
|
"label.participation-reference": "reference",
|
|
48
51
|
"label.picture": "picture",
|
|
@@ -59,6 +62,9 @@
|
|
|
59
62
|
"text.department-not-selected": "no department is selected",
|
|
60
63
|
"text.department-with-children-cannot-be-deleted": "a department with sub-departments cannot be deleted",
|
|
61
64
|
"text.departments are made on the department screen": "departments are made on the department screen",
|
|
65
|
+
"text.employee-type-fulltime": "full-time",
|
|
66
|
+
"text.employee-type-parttime": "part-time",
|
|
67
|
+
"text.employee-type-temporary": "temporary",
|
|
62
68
|
"text.no departments yet": "no departments yet",
|
|
63
69
|
"text.no-common-approval-line-description": "a shared approval line is a route the organization sets. everyone can pick it.",
|
|
64
70
|
"text.no-common-approval-line-title": "no shared approval lines",
|
|
@@ -69,6 +75,14 @@
|
|
|
69
75
|
"text.notify-after-nth": "after approval {n}",
|
|
70
76
|
"text.notify-after-only-reference": "only for reference",
|
|
71
77
|
"text.notify-immediately": "as soon as submitted",
|
|
78
|
+
"text.organization-entry-department-control-no-exists": "a department with this code already exists",
|
|
79
|
+
"text.organization-entry-department-name-exists": "a department with this name already exists",
|
|
80
|
+
"text.organization-entry-department-parent-first": "the parent department {parent} does not exist yet. enter the parent first",
|
|
81
|
+
"text.organization-entry-department-parent-loop": "a department cannot be placed under itself or one of its sub-departments",
|
|
82
|
+
"text.organization-entry-department-parent-not-found": "the parent department is not found",
|
|
83
|
+
"text.organization-entry-department-repeated": "same name or code as an earlier row",
|
|
84
|
+
"text.organization-entry-employee-exists": "an employee with this number already exists",
|
|
85
|
+
"text.organization-entry-employee-repeated": "same number as an earlier row",
|
|
72
86
|
"title.approval-line": "approval lines",
|
|
73
87
|
"title.approval-line item list": "approval line item list",
|
|
74
88
|
"title.approval-line template list": "approval line template list",
|
package/translations/ja.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"field.approval-line": "決裁ライン",
|
|
15
15
|
"field.approver": "決裁者",
|
|
16
16
|
"field.control-no": "管理番号",
|
|
17
|
+
"field.extension": "内線番号",
|
|
17
18
|
"field.hired-on": "入社日",
|
|
18
19
|
"field.note": "コメント",
|
|
19
20
|
"field.notify-after": "通知時点",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"label.manager": "管理者",
|
|
44
45
|
"label.myself": "私",
|
|
45
46
|
"label.note": "ノート",
|
|
47
|
+
"label.organization-entry-department": "部署",
|
|
48
|
+
"label.organization-entry-employee": "従業員",
|
|
46
49
|
"label.participation-approval": "決裁",
|
|
47
50
|
"label.participation-reference": "参照",
|
|
48
51
|
"label.picture": "写真",
|
|
@@ -59,6 +62,9 @@
|
|
|
59
62
|
"text.department-not-selected": "部署が選択されていません",
|
|
60
63
|
"text.department-with-children-cannot-be-deleted": "下位部署がある部署は削除できません",
|
|
61
64
|
"text.departments are made on the department screen": "部署は部署画面で作成します",
|
|
65
|
+
"text.employee-type-fulltime": "正社員",
|
|
66
|
+
"text.employee-type-parttime": "パートタイム",
|
|
67
|
+
"text.employee-type-temporary": "臨時",
|
|
62
68
|
"text.no departments yet": "部署がまだありません",
|
|
63
69
|
"text.no-common-approval-line-description": "共用の決裁ラインは組織が定める決裁経路です。全員が選んで使えます。",
|
|
64
70
|
"text.no-common-approval-line-title": "共用の決裁ラインがありません",
|
|
@@ -69,6 +75,14 @@
|
|
|
69
75
|
"text.notify-after-nth": "{n}番目の決裁後",
|
|
70
76
|
"text.notify-after-only-reference": "参照にのみ使います",
|
|
71
77
|
"text.notify-immediately": "申請直後",
|
|
78
|
+
"text.organization-entry-department-control-no-exists": "同じ管理番号の部署がすでにあります",
|
|
79
|
+
"text.organization-entry-department-name-exists": "同じ名前の部署がすでにあります",
|
|
80
|
+
"text.organization-entry-department-parent-first": "上位部署 {parent} はまだありません。上位部署を先に入力してください",
|
|
81
|
+
"text.organization-entry-department-parent-loop": "自分自身や下位部署の下には移せません",
|
|
82
|
+
"text.organization-entry-department-parent-not-found": "上位部署が見つかりません",
|
|
83
|
+
"text.organization-entry-department-repeated": "前の行と名前または管理番号が同じです",
|
|
84
|
+
"text.organization-entry-employee-exists": "同じ管理番号の従業員がすでにいます",
|
|
85
|
+
"text.organization-entry-employee-repeated": "前の行と管理番号が同じです",
|
|
72
86
|
"title.approval-line": "決裁ライン",
|
|
73
87
|
"title.approval-line item list": "決裁ライン詳細",
|
|
74
88
|
"title.approval-line template list": "決裁ライン テンプレート リスト",
|
package/translations/ko.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"field.approval-line": "결재선",
|
|
15
15
|
"field.approver": "결재자",
|
|
16
16
|
"field.control-no": "관리 번호",
|
|
17
|
+
"field.extension": "내선 번호",
|
|
17
18
|
"field.hired-on": "입사일",
|
|
18
19
|
"field.note": "코멘트",
|
|
19
20
|
"field.notify-after": "알림 시점",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"label.manager": "관리자",
|
|
44
45
|
"label.myself": "나",
|
|
45
46
|
"label.note": "노트",
|
|
47
|
+
"label.organization-entry-department": "부서",
|
|
48
|
+
"label.organization-entry-employee": "직원",
|
|
46
49
|
"label.participation-approval": "결재",
|
|
47
50
|
"label.participation-reference": "참조",
|
|
48
51
|
"label.picture": "사진",
|
|
@@ -59,6 +62,9 @@
|
|
|
59
62
|
"text.department-not-selected": "선택한 부서가 없습니다",
|
|
60
63
|
"text.department-with-children-cannot-be-deleted": "하위 부서가 있는 부서는 삭제할 수 없습니다",
|
|
61
64
|
"text.departments are made on the department screen": "부서는 부서 화면에서 만듭니다",
|
|
65
|
+
"text.employee-type-fulltime": "정규직",
|
|
66
|
+
"text.employee-type-parttime": "시간제",
|
|
67
|
+
"text.employee-type-temporary": "임시직",
|
|
62
68
|
"text.no departments yet": "아직 부서가 없습니다",
|
|
63
69
|
"text.no-common-approval-line-description": "공용 결재선은 조직이 정해 두는 결재 경로입니다. 모두가 골라 쓸 수 있습니다.",
|
|
64
70
|
"text.no-common-approval-line-title": "공용 결재선이 없습니다",
|
|
@@ -69,6 +75,14 @@
|
|
|
69
75
|
"text.notify-after-nth": "{n}번째 결재 뒤",
|
|
70
76
|
"text.notify-after-only-reference": "참조에만 씁니다",
|
|
71
77
|
"text.notify-immediately": "상신 즉시",
|
|
78
|
+
"text.organization-entry-department-control-no-exists": "같은 관리번호의 부서가 이미 있습니다",
|
|
79
|
+
"text.organization-entry-department-name-exists": "같은 이름의 부서가 이미 있습니다",
|
|
80
|
+
"text.organization-entry-department-parent-first": "상위 부서({parent})가 아직 없습니다. 상위 부서를 먼저 입력하십시오",
|
|
81
|
+
"text.organization-entry-department-parent-loop": "자기 자신이나 하위 부서 아래로 옮길 수 없습니다",
|
|
82
|
+
"text.organization-entry-department-parent-not-found": "상위 부서를 찾을 수 없습니다",
|
|
83
|
+
"text.organization-entry-department-repeated": "앞 행과 이름 또는 관리번호가 같습니다",
|
|
84
|
+
"text.organization-entry-employee-exists": "같은 관리번호의 직원이 이미 있습니다",
|
|
85
|
+
"text.organization-entry-employee-repeated": "앞 행과 관리번호가 같습니다",
|
|
72
86
|
"title.approval-line": "결재선",
|
|
73
87
|
"title.approval-line item list": "결재선 상세",
|
|
74
88
|
"title.approval-line template list": "결재선 목록",
|
package/translations/ms.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"field.approval-line": "talian kelulusan",
|
|
15
15
|
"field.approver": "penyetuju",
|
|
16
16
|
"field.control-no": "nombor kawalan",
|
|
17
|
+
"field.extension": "sambungan",
|
|
17
18
|
"field.hired-on": "diberhentikan pada",
|
|
18
19
|
"field.note": "catatan",
|
|
19
20
|
"field.notify-after": "masa notis",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"label.manager": "pengurus",
|
|
44
45
|
"label.myself": "diri saya",
|
|
45
46
|
"label.note": "catatan",
|
|
47
|
+
"label.organization-entry-department": "jabatan",
|
|
48
|
+
"label.organization-entry-employee": "pekerja",
|
|
46
49
|
"label.participation-approval": "kelulusan",
|
|
47
50
|
"label.participation-reference": "rujukan",
|
|
48
51
|
"label.picture": "gambar",
|
|
@@ -59,6 +62,9 @@
|
|
|
59
62
|
"text.department-not-selected": "tiada jabatan dipilih",
|
|
60
63
|
"text.department-with-children-cannot-be-deleted": "jabatan yang mempunyai sub-jabatan tidak boleh dipadam",
|
|
61
64
|
"text.departments are made on the department screen": "jabatan dibuat pada skrin jabatan",
|
|
65
|
+
"text.employee-type-fulltime": "sepenuh masa",
|
|
66
|
+
"text.employee-type-parttime": "separuh masa",
|
|
67
|
+
"text.employee-type-temporary": "sementara",
|
|
62
68
|
"text.no departments yet": "belum ada jabatan",
|
|
63
69
|
"text.no-common-approval-line-description": "talian kelulusan kongsi ialah laluan yang ditetapkan organisasi. semua orang boleh memilihnya.",
|
|
64
70
|
"text.no-common-approval-line-title": "tiada talian kelulusan kongsi",
|
|
@@ -69,6 +75,14 @@
|
|
|
69
75
|
"text.notify-after-nth": "selepas kelulusan {n}",
|
|
70
76
|
"text.notify-after-only-reference": "hanya untuk rujukan",
|
|
71
77
|
"text.notify-immediately": "sebaik dihantar",
|
|
78
|
+
"text.organization-entry-department-control-no-exists": "jabatan dengan kod ini sudah wujud",
|
|
79
|
+
"text.organization-entry-department-name-exists": "jabatan dengan nama ini sudah wujud",
|
|
80
|
+
"text.organization-entry-department-parent-first": "jabatan induk {parent} belum wujud. masukkan jabatan induk dahulu",
|
|
81
|
+
"text.organization-entry-department-parent-loop": "jabatan tidak boleh diletakkan di bawah dirinya atau subjabatannya",
|
|
82
|
+
"text.organization-entry-department-parent-not-found": "jabatan induk tidak ditemui",
|
|
83
|
+
"text.organization-entry-department-repeated": "nama atau kod sama dengan baris sebelumnya",
|
|
84
|
+
"text.organization-entry-employee-exists": "pekerja dengan nombor ini sudah wujud",
|
|
85
|
+
"text.organization-entry-employee-repeated": "nombor sama dengan baris sebelumnya",
|
|
72
86
|
"title.approval-line": "talian kelulusan",
|
|
73
87
|
"title.approval-line item list": "senarai item garis kelulusan",
|
|
74
88
|
"title.approval-line template list": "senarai templat garis kelulusan",
|
package/translations/zh.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"field.approval-line": "审批线",
|
|
15
15
|
"field.approver": "审批人",
|
|
16
16
|
"field.control-no": "控制编号",
|
|
17
|
+
"field.extension": "分机号",
|
|
17
18
|
"field.hired-on": "入职日期",
|
|
18
19
|
"field.note": "备注",
|
|
19
20
|
"field.notify-after": "通知时点",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"label.manager": "经理",
|
|
44
45
|
"label.myself": "我",
|
|
45
46
|
"label.note": "备注",
|
|
47
|
+
"label.organization-entry-department": "部门",
|
|
48
|
+
"label.organization-entry-employee": "员工",
|
|
46
49
|
"label.participation-approval": "审批",
|
|
47
50
|
"label.participation-reference": "知会",
|
|
48
51
|
"label.picture": "照片",
|
|
@@ -59,6 +62,9 @@
|
|
|
59
62
|
"text.department-not-selected": "未选择部门",
|
|
60
63
|
"text.department-with-children-cannot-be-deleted": "有下级部门的部门不能删除",
|
|
61
64
|
"text.departments are made on the department screen": "部门在部门页面创建",
|
|
65
|
+
"text.employee-type-fulltime": "全职",
|
|
66
|
+
"text.employee-type-parttime": "兼职",
|
|
67
|
+
"text.employee-type-temporary": "临时",
|
|
62
68
|
"text.no departments yet": "尚无部门",
|
|
63
69
|
"text.no-common-approval-line-description": "共用审批线是组织设定的审批路径,所有人都可以选用。",
|
|
64
70
|
"text.no-common-approval-line-title": "没有共用审批线",
|
|
@@ -69,6 +75,14 @@
|
|
|
69
75
|
"text.notify-after-nth": "第{n}个审批后",
|
|
70
76
|
"text.notify-after-only-reference": "仅用于知会",
|
|
71
77
|
"text.notify-immediately": "提交后立即",
|
|
78
|
+
"text.organization-entry-department-control-no-exists": "已有相同管理编号的部门",
|
|
79
|
+
"text.organization-entry-department-name-exists": "已有同名部门",
|
|
80
|
+
"text.organization-entry-department-parent-first": "上级部门 {parent} 尚不存在。请先录入上级部门",
|
|
81
|
+
"text.organization-entry-department-parent-loop": "不能移到自身或其下级部门之下",
|
|
82
|
+
"text.organization-entry-department-parent-not-found": "找不到上级部门",
|
|
83
|
+
"text.organization-entry-department-repeated": "名称或管理编号与前面的行相同",
|
|
84
|
+
"text.organization-entry-employee-exists": "已有相同管理编号的员工",
|
|
85
|
+
"text.organization-entry-employee-repeated": "管理编号与前面的行相同",
|
|
72
86
|
"title.approval-line": "审批线",
|
|
73
87
|
"title.approval-line item list": "审批线项目列表",
|
|
74
88
|
"title.approval-line template list": "审批线模板列表",
|