@things-factory/dataset 6.2.29 → 6.2.31

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 (64) hide show
  1. package/client/activities/activity-data-collect-assign-edit.ts +99 -0
  2. package/client/activities/activity-data-collect-assign-view.ts +99 -0
  3. package/client/bootstrap.ts +2 -1
  4. package/client/pages/data-entry/data-entry-list-page.ts +17 -23
  5. package/client/pages/data-ooc/data-ooc-list-page.ts +16 -31
  6. package/client/pages/data-sensor/data-sensor-list-page.ts +40 -32
  7. package/client/pages/data-set/data-set-list-page.ts +23 -12
  8. package/client/pages/data-summary/data-summary-list-page.ts +15 -30
  9. package/client/pages/data-summary/data-summary-period-page.ts +0 -11
  10. package/client/pages/data-summary/data-summary-search-page.ts +12 -27
  11. package/dist-client/activities/activity-data-collect-assign-edit.d.ts +14 -0
  12. package/dist-client/activities/activity-data-collect-assign-edit.js +102 -0
  13. package/dist-client/activities/activity-data-collect-assign-edit.js.map +1 -0
  14. package/dist-client/activities/activity-data-collect-assign-view.d.ts +14 -0
  15. package/dist-client/activities/activity-data-collect-assign-view.js +102 -0
  16. package/dist-client/activities/activity-data-collect-assign-view.js.map +1 -0
  17. package/dist-client/bootstrap.d.ts +2 -0
  18. package/dist-client/bootstrap.js +2 -0
  19. package/dist-client/bootstrap.js.map +1 -1
  20. package/dist-client/pages/data-entry/data-entry-list-page.d.ts +3 -2
  21. package/dist-client/pages/data-entry/data-entry-list-page.js +16 -26
  22. package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
  23. package/dist-client/pages/data-ooc/data-ooc-list-page.d.ts +5 -4
  24. package/dist-client/pages/data-ooc/data-ooc-list-page.js +13 -32
  25. package/dist-client/pages/data-ooc/data-ooc-list-page.js.map +1 -1
  26. package/dist-client/pages/data-sensor/data-sensor-list-page.d.ts +12 -5
  27. package/dist-client/pages/data-sensor/data-sensor-list-page.js +41 -35
  28. package/dist-client/pages/data-sensor/data-sensor-list-page.js.map +1 -1
  29. package/dist-client/pages/data-set/data-set-list-page.js +22 -3
  30. package/dist-client/pages/data-set/data-set-list-page.js.map +1 -1
  31. package/dist-client/pages/data-summary/data-summary-list-page.d.ts +4 -3
  32. package/dist-client/pages/data-summary/data-summary-list-page.js +18 -37
  33. package/dist-client/pages/data-summary/data-summary-list-page.js.map +1 -1
  34. package/dist-client/pages/data-summary/data-summary-period-page.js +0 -11
  35. package/dist-client/pages/data-summary/data-summary-period-page.js.map +1 -1
  36. package/dist-client/pages/data-summary/data-summary-search-page.d.ts +2 -1
  37. package/dist-client/pages/data-summary/data-summary-search-page.js +12 -31
  38. package/dist-client/pages/data-summary/data-summary-search-page.js.map +1 -1
  39. package/dist-client/tsconfig.tsbuildinfo +1 -1
  40. package/dist-server/activities/activity-data-collect-assign.js +85 -0
  41. package/dist-server/activities/activity-data-collect-assign.js.map +1 -0
  42. package/dist-server/activities/activity-ooc-review.js +3 -2
  43. package/dist-server/activities/activity-ooc-review.js.map +1 -1
  44. package/dist-server/controllers/create-data-sample.js +59 -5
  45. package/dist-server/controllers/create-data-sample.js.map +1 -1
  46. package/dist-server/service/data-set/data-set-mutation.js +36 -6
  47. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  48. package/dist-server/service/data-set/data-set-type.js +8 -0
  49. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  50. package/dist-server/service/data-set/data-set.js +10 -2
  51. package/dist-server/service/data-set/data-set.js.map +1 -1
  52. package/dist-server/tsconfig.tsbuildinfo +1 -1
  53. package/package.json +10 -10
  54. package/server/activities/activity-data-collect-assign.ts +98 -0
  55. package/server/activities/activity-ooc-review.ts +4 -3
  56. package/server/controllers/create-data-sample.ts +64 -6
  57. package/server/service/data-set/data-set-mutation.ts +41 -3
  58. package/server/service/data-set/data-set-type.ts +7 -1
  59. package/server/service/data-set/data-set.ts +9 -2
  60. package/translations/en.json +1 -0
  61. package/translations/ja.json +1 -0
  62. package/translations/ko.json +1 -0
  63. package/translations/ms.json +1 -0
  64. package/translations/zh.json +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/dataset",
3
- "version": "6.2.29",
3
+ "version": "6.2.31",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -37,19 +37,19 @@
37
37
  "@operato/shell": "^1.0.1",
38
38
  "@operato/styles": "^1.0.0",
39
39
  "@operato/utils": "^1.0.1",
40
- "@things-factory/auth-base": "^6.2.27",
41
- "@things-factory/aws-base": "^6.2.27",
42
- "@things-factory/board-service": "^6.2.27",
40
+ "@things-factory/auth-base": "^6.2.31",
41
+ "@things-factory/aws-base": "^6.2.31",
42
+ "@things-factory/board-service": "^6.2.31",
43
43
  "@things-factory/env": "^6.2.27",
44
- "@things-factory/organization": "^6.2.27",
45
- "@things-factory/scheduler-client": "^6.2.27",
46
- "@things-factory/shell": "^6.2.27",
47
- "@things-factory/work-shift": "^6.2.27",
48
- "@things-factory/worklist": "^6.2.27",
44
+ "@things-factory/organization": "^6.2.31",
45
+ "@things-factory/scheduler-client": "^6.2.31",
46
+ "@things-factory/shell": "^6.2.31",
47
+ "@things-factory/work-shift": "^6.2.31",
48
+ "@things-factory/worklist": "^6.2.31",
49
49
  "cron-parser": "^4.3.0",
50
50
  "moment-timezone": "^0.5.40",
51
51
  "simple-statistics": "^7.8.3",
52
52
  "statistics": "^3.3.0"
53
53
  },
54
- "gitHead": "4c35448744c3fd288a014a4f9923004797bf4724"
54
+ "gitHead": "0e904fde521867b12fb95fc24f98ca5e6bd53e6e"
55
55
  }
@@ -0,0 +1,98 @@
1
+ import { NewDataSample } from 'service/data-sample/data-sample-type'
2
+ import { In } from 'typeorm'
3
+
4
+ import {
5
+ Activity,
6
+ ActivityInstance,
7
+ ActivityInstanceStatus,
8
+ UpdateActivityInstanceStateAddendum
9
+ } from '@things-factory/worklist'
10
+
11
+ import { createDataSample } from '../controllers/create-data-sample'
12
+
13
+ async function callback(
14
+ activityInstance: ActivityInstance,
15
+ addendum: UpdateActivityInstanceStateAddendum,
16
+ context: ResolverContext
17
+ ) {
18
+ const { domain, tx } = context.state
19
+ const { input, output, state } = activityInstance
20
+
21
+ if (state == ActivityInstanceStatus.Ended) {
22
+ const activity = (await tx.getRepository(Activity).findOneBy({
23
+ domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
24
+ name: 'Collect Assign Data'
25
+ })) as Activity
26
+
27
+ const data = output
28
+
29
+ if (activity) {
30
+ const dataSample: NewDataSample = {
31
+ dataSet: {
32
+ id: input.dataSetId
33
+ },
34
+ data
35
+ }
36
+
37
+ await createDataSample(dataSample, context)
38
+ } else {
39
+ console.error('Collect Assign Data Activity not installed.')
40
+ }
41
+ }
42
+ }
43
+
44
+ export const ActivityDataCollectAssign = {
45
+ name: 'Collect Assign Data',
46
+ description: 'Tasks assigned for data collection',
47
+ release: '1.0.0',
48
+ provider: 'hatiolab.com',
49
+ category: 'quality',
50
+ activityType: 'user',
51
+ priority: 1,
52
+ searchKeys: [
53
+ {
54
+ name: 'dataSetName',
55
+ description: 'DataSet name',
56
+ inputKey: 'dataSetName',
57
+ tKey: 'data-set-name'
58
+ }
59
+ ],
60
+ startingType: '',
61
+ multiple: 'single',
62
+ model: [
63
+ {
64
+ name: 'dataSetId',
65
+ description: 'DataSet Id.',
66
+ tag: 'dataSetId',
67
+ hidden: true,
68
+ mandatory: true,
69
+ inout: 'in',
70
+ type: 'string',
71
+ options: {},
72
+ unit: null,
73
+ quantifier: [1],
74
+ spec: {}
75
+ },
76
+ {
77
+ name: 'dataSetName',
78
+ description: 'DataSet name',
79
+ tag: 'dataSetName',
80
+ hidden: true,
81
+ mandatory: true,
82
+ inout: 'in',
83
+ type: 'string',
84
+ options: {},
85
+ unit: null,
86
+ quantifier: [1],
87
+ spec: {}
88
+ }
89
+ ],
90
+ uiType: 'custom-element',
91
+ uiSource: 'activity-data-collect-assign-edit',
92
+ viewType: 'custom-element',
93
+ viewSource: 'activity-data-collect-assign-view',
94
+ reportType: 'custom-element',
95
+ reportSource: 'activity-data-collect-assign-view',
96
+ thumbnail: '/assets/images/data-collect.png',
97
+ callback /* Called when there is a change in the lifecycle of a task (activity-instance). */
98
+ }
@@ -2,10 +2,10 @@ import {
2
2
  Activity,
3
3
  ActivityInstance,
4
4
  ActivityInstanceStatus,
5
- ActivityThread,
6
5
  UpdateActivityInstanceStateAddendum
7
6
  } from '@things-factory/worklist'
8
7
  import { issue } from '@things-factory/worklist/dist-server/controllers/activity-instance/issue'
8
+
9
9
  import { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'
10
10
 
11
11
  async function callback(
@@ -36,7 +36,7 @@ async function callback(
36
36
  })
37
37
 
38
38
  const dataSet = dataOoc.dataSet
39
- const { assignees, approvalLine } = dataSet
39
+ const { assignees, approvalLine, outlierApprovalLine } = dataSet
40
40
 
41
41
  /* dataOoc Review 결과를 dataOoc 엔티티에 반영한다. */
42
42
  dataOoc = await tx.getRepository(DataOoc).save({
@@ -59,7 +59,8 @@ async function callback(
59
59
  instruction: correctiveInstruction
60
60
  },
61
61
  assignees,
62
- approvalLine
62
+ approvalLine,
63
+ outlierApprovalLine
63
64
  }
64
65
 
65
66
  await issue(activityInstance, context)
@@ -1,20 +1,19 @@
1
1
  import moment from 'moment-timezone'
2
2
  import { In } from 'typeorm'
3
3
 
4
+ import { Attachment, createAttachment } from '@things-factory/attachment-base'
4
5
  import { Role } from '@things-factory/auth-base'
5
- import { getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
6
6
  import { logger } from '@things-factory/env'
7
+ import { getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
7
8
  import { getWorkDateAndShift } from '@things-factory/work-shift'
9
+ import { Activity } from '@things-factory/worklist'
10
+ import { issue } from '@things-factory/worklist/dist-server/controllers/activity-instance/issue'
8
11
 
9
12
  import { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'
10
13
  import { DataSample } from '../service/data-sample/data-sample'
11
14
  import { NewDataSample } from '../service/data-sample/data-sample-type'
12
15
  import { DataSet } from '../service/data-set/data-set'
13
16
  import { DataUseCase } from './data-use-case'
14
- import { Activity } from '@things-factory/worklist'
15
- import { Attachment, createAttachment } from '@things-factory/attachment-base'
16
-
17
- import { issue } from '@things-factory/worklist/dist-server/controllers/activity-instance/issue'
18
17
 
19
18
  // See README.md at ## Data Samples
20
19
  process.env.TZ = 'UTC'
@@ -230,7 +229,8 @@ export async function createDataSample(dataSample: NewDataSample, context: Resol
230
229
  dataOocId: dataOoc.id
231
230
  },
232
231
  assignees,
233
- approvalLine: []
232
+ approvalLine: [],
233
+ outlierApprovalLine: []
234
234
  }
235
235
 
236
236
  await issue(activityInstance, context)
@@ -262,6 +262,64 @@ export async function createDataSample(dataSample: NewDataSample, context: Resol
262
262
  } catch (err) {
263
263
  logger.error('Notification', err)
264
264
  }
265
+ } else {
266
+ const activity = (await tx.getRepository(Activity).findOneBy({
267
+ domain: { id: domain.id },
268
+ name: 'Collect Assign Data'
269
+ })) as Activity
270
+
271
+ if (activity) {
272
+ const assignee =
273
+ dataSet.supervisoryRoleId &&
274
+ (await tx.getRepository(Role).findOneBy({ domain: { id: domain.id }, id: dataSet.supervisoryRoleId }))
275
+
276
+ const assignees = dataSet.supervisoryRoleId ? [{ type: 'Role', value: dataSet.supervisoryRoleId, assignee }] : []
277
+
278
+ /* 해당 dataset의 supervisor로 하여금, OOC를 리뷰하고 instruction을 작성해서, OOC 해결을 위한 태스크를 dataset assignees에게 지시하도록 한다. */
279
+ if (assignees && assignees instanceof Array && assignees.length > 0) {
280
+ result.dataItems = dataItems
281
+ const activityInstance = {
282
+ name: `[Data Collect 검토] ${dataSet.name}`,
283
+ description: dataSet.description,
284
+ activityId: activity.id,
285
+ dueAt: new Date(collectedAt.getTime() + 24 * 60 * 60 * 1000),
286
+ input: {
287
+ dataSample: result
288
+ },
289
+ assignees,
290
+ approvalLine: [],
291
+ outlierApprovalLine: []
292
+ }
293
+
294
+ await issue(activityInstance, context)
295
+ } else {
296
+ console.error(
297
+ `Assignees are not set. So Data Collect Review task for ${dataSet.name}(${dataSet.id}) could not be issued.`
298
+ )
299
+ }
300
+ } else {
301
+ console.error('Data Collect Review Activity not installed.')
302
+ }
303
+
304
+ try {
305
+ pubsub.publish('data-set', {
306
+ dataSet,
307
+ supervisoryRoleId: dataSet.supervisoryRoleId
308
+ })
309
+
310
+ pubsub.publish('notification', {
311
+ notification: {
312
+ domain,
313
+ type: 'info',
314
+ title: `[Data Collect Review] ${dataSet.name}`,
315
+ body: `Data Collect Review occurred on '${dataSet.name}'`,
316
+ url: getRedirectSubdomainPath(context, domain.subdomain, `/data-set/${dataSet.id}`),
317
+ timestamp: collectedAt
318
+ }
319
+ })
320
+ } catch (err) {
321
+ logger.error('Notification', err)
322
+ }
265
323
  }
266
324
 
267
325
  return result
@@ -2,13 +2,13 @@ import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
2
2
  import { In } from 'typeorm'
3
3
 
4
4
  import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
5
- import { Application, CallbackBase, registerSchedule, unregisterSchedule } from '@things-factory/scheduler-client'
6
5
  import { ApprovalLineItem, OrgMemberTargetType } from '@things-factory/organization'
6
+ import { Application, CallbackBase, registerSchedule, unregisterSchedule } from '@things-factory/scheduler-client'
7
7
  import { AssigneeItem } from '@things-factory/worklist'
8
8
 
9
- import { DataSet, DataSetSummaryPeriodType } from './data-set'
10
- import { DataSetPatch, NewDataSet } from './data-set-type'
11
9
  import { getDataSummaryCrontabSchedule } from '../../controllers/generate-data-summary'
10
+ import { DataSet } from './data-set'
11
+ import { DataSetPatch, NewDataSet } from './data-set-type'
12
12
 
13
13
  const crypto = require('crypto')
14
14
 
@@ -45,6 +45,40 @@ function getApprovalLineValue(patch: NewDataSet | DataSetPatch): ApprovalLineIte
45
45
  })
46
46
  }
47
47
 
48
+ function getOutlierApprovalLineValue(patch: NewDataSet | DataSetPatch): ApprovalLineItem[] {
49
+ const { outlierApprovalLine } = patch
50
+
51
+ if (!('outlierApprovalLine' in patch)) {
52
+ /* approvalLine이 언급되지 않았다면, 업데이트하지 않는다. */
53
+ return
54
+ }
55
+
56
+ if (!outlierApprovalLine || !(outlierApprovalLine instanceof Array)) {
57
+ /* approvalLine의 값이 없거나 배열이 아니라면, 클리어시킨다. */
58
+ return null
59
+ }
60
+
61
+ return outlierApprovalLine
62
+ .map(m => {
63
+ return {
64
+ type: m.type,
65
+ approver: m.approver
66
+ } as ApprovalLineItem
67
+ })
68
+ .filter(m => m.type)
69
+ .filter(m => {
70
+ switch (m.type) {
71
+ case OrgMemberTargetType.Employee:
72
+ case OrgMemberTargetType.Department:
73
+ case OrgMemberTargetType.Role:
74
+ return !!m.approver?.id
75
+ default:
76
+ return true
77
+ }
78
+ })
79
+ }
80
+
81
+
48
82
  function getAssigneesValue(patch: NewDataSet | DataSetPatch): AssigneeItem[] {
49
83
  const { assignees } = patch
50
84
 
@@ -91,6 +125,7 @@ export class DataSetMutation {
91
125
  ...dataSet,
92
126
  approvalLine: getApprovalLineValue(dataSet),
93
127
  assignees: getAssigneesValue(dataSet),
128
+ outlierApprovalLineValue: getOutlierApprovalLineValue(dataSet),
94
129
  version: 1,
95
130
  domain,
96
131
  creator: user,
@@ -123,6 +158,7 @@ export class DataSetMutation {
123
158
  ...dataSet,
124
159
  ...patch,
125
160
  approvalLine: getApprovalLineValue(patch),
161
+ outlierApprovalLineValue: getOutlierApprovalLineValue(patch),
126
162
  assignees: getAssigneesValue(patch),
127
163
  updater: user
128
164
  })
@@ -155,6 +191,7 @@ export class DataSetMutation {
155
191
  ...newRecord,
156
192
  approvalLine: getApprovalLineValue(newRecord),
157
193
  assignees: getAssigneesValue(newRecord),
194
+ outlierApprovalLineValue: getOutlierApprovalLineValue(newRecord),
158
195
  domain,
159
196
  creator: user,
160
197
  updater: user
@@ -183,6 +220,7 @@ export class DataSetMutation {
183
220
  ...dataSet,
184
221
  ...updateRecord,
185
222
  approvalLine: getApprovalLineValue(updateRecord),
223
+ outlierApprovalLineValue: getOutlierApprovalLineValue(updateRecord),
186
224
  assignees: getAssigneesValue(updateRecord),
187
225
  updater: user
188
226
  })
@@ -2,8 +2,8 @@ import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'
2
2
  import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
3
3
  import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'
4
4
 
5
- import { ObjectRef, ScalarObject } from '@things-factory/shell'
6
5
  import { ApprovalLineItem } from '@things-factory/organization'
6
+ import { ObjectRef, ScalarObject } from '@things-factory/shell'
7
7
  import { AssigneeItem } from '@things-factory/worklist'
8
8
 
9
9
  import { DataItemPatch } from './data-item-type'
@@ -32,6 +32,9 @@ export class NewDataSet {
32
32
  @Field(type => ScalarObject, { nullable: true })
33
33
  approvalLine?: ApprovalLineItem[]
34
34
 
35
+ @Field(type => ScalarObject, { nullable: true })
36
+ outlierApprovalLine?: ApprovalLineItem[]
37
+
35
38
  @Field({ nullable: true })
36
39
  active?: boolean
37
40
 
@@ -104,6 +107,9 @@ export class DataSetPatch {
104
107
  @Field(type => ScalarObject, { nullable: true })
105
108
  approvalLine?: ApprovalLineItem[]
106
109
 
110
+ @Field(type => ScalarObject, { nullable: true })
111
+ outlierApprovalLine?: ApprovalLineItem[]
112
+
107
113
  @Field({ nullable: true })
108
114
  active?: boolean
109
115
 
@@ -12,8 +12,8 @@ import {
12
12
  } from 'typeorm'
13
13
 
14
14
  import { Role, User } from '@things-factory/auth-base'
15
- import { Domain, ScalarObject } from '@things-factory/shell'
16
15
  import { ApprovalLineItem } from '@things-factory/organization'
16
+ import { Domain, ScalarObject } from '@things-factory/shell'
17
17
  import { AssigneeItem } from '@things-factory/worklist'
18
18
 
19
19
  import { DataKeySet } from '../data-key-set/data-key-set'
@@ -152,10 +152,17 @@ export class DataSet {
152
152
  @Column('simple-json', { nullable: true })
153
153
  @Field(type => [ApprovalLineItem], {
154
154
  nullable: true,
155
- description: 'Approval line for handling OOC issues for that dataset'
155
+ description: 'Approval line for confrim of the dataset'
156
156
  })
157
157
  approvalLine?: ApprovalLineItem[]
158
158
 
159
+ @Column('simple-json', { nullable: true })
160
+ @Field(type => [ApprovalLineItem], {
161
+ nullable: true,
162
+ description: 'Approval line for handling OOC issues for that dataset'
163
+ })
164
+ outlierApprovalLine?: ApprovalLineItem[]
165
+
159
166
  @Column({ nullable: true })
160
167
  @Field({ nullable: true })
161
168
  type?: 'manual' | 'automatic' | string
@@ -7,6 +7,7 @@
7
7
  "error.summary not supported": "The given dataSet({dataSetName}) does not support reports for {period}",
8
8
  "field.appliance": "appliance",
9
9
  "field.approval-line": "approval line",
10
+ "field.outlier-approval-line": "outlier approval line",
10
11
  "field.assignees": "assignees",
11
12
  "field.collected-at": "collected at",
12
13
  "field.corrected-at": "corrected at",
@@ -6,6 +6,7 @@
6
6
  "error.summary not supported": "指定されたデータセット({dataSetName})は{period}のレポートに対応していません",
7
7
  "field.appliance": "アプライアンス",
8
8
  "field.approval-line": "決済ライン",
9
+ "field.outlier-approval-line": "異常承認ライン",
9
10
  "field.assignees": "譲渡人リスト",
10
11
  "field.collected-at": "取集日時",
11
12
  "field.corrected-at": "措置時間",
@@ -6,6 +6,7 @@
6
6
  "error.summary not supported": "주어진 dataSet({dataSetName})은 {period}의 레포트가 지원되지 않습니다",
7
7
  "field.appliance": "어플라이언스",
8
8
  "field.approval-line": "결재라인",
9
+ "field.outlier-approval-line": "이상치 결재라인",
9
10
  "field.assignees": "할당자 목록",
10
11
  "field.collected-at": "수집일시",
11
12
  "field.corrected-at": "조치 시간",
@@ -6,6 +6,7 @@
6
6
  "error.summary not supported": "DataSet yang diberikan({dataSetName}) tidak menyokong laporan untuk {period}",
7
7
  "field.appliance": "appliance",
8
8
  "field.approval-line": "approval line",
9
+ "field.outlier-approval-line": "outlier approval line",
9
10
  "field.assignees": "assignees",
10
11
  "field.collected-at": "collected at",
11
12
  "field.corrected-at": "corrected at",
@@ -6,6 +6,7 @@
6
6
  "error.summary not supported": "给定的数据集({dataSetName})不支持{period}的报告",
7
7
  "field.appliance": "应用设备",
8
8
  "field.approval-line": "审批线",
9
+ "field.outlier-approval-line": "异常批准线",
9
10
  "field.assignees": "分配者列表",
10
11
  "field.collected-at": "收集时间",
11
12
  "field.corrected-at": "处理时间",