@things-factory/kpi 9.1.19 → 10.0.0-beta.2

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 (140) hide show
  1. package/client/pages/kpi/kpi-list-page.ts +339 -525
  2. package/client/pages/kpi/kpi-tree-page.ts +135 -207
  3. package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
  4. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
  5. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
  6. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
  7. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
  8. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
  9. package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
  10. package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
  11. package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
  12. package/dist-client/pages/kpi/kpi-list-page.js +150 -282
  13. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  14. package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
  15. package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
  16. package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
  17. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
  18. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
  19. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  20. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
  21. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
  22. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  23. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
  24. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
  25. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  26. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
  27. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
  28. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  29. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
  30. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
  31. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  32. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
  33. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
  34. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  35. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
  36. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
  37. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  38. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
  39. package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
  40. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  41. package/dist-client/tsconfig.tsbuildinfo +1 -1
  42. package/dist-server/service/index.d.ts +1 -1
  43. package/dist-server/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +18 -18
  45. package/client/tsconfig.json +0 -11
  46. package/dist-server/tsconfig.json +0 -10
  47. package/server/@types/index.d.ts +0 -11
  48. package/server/calculator/evaluator.ts +0 -45
  49. package/server/calculator/functions.ts +0 -67
  50. package/server/calculator/index.ts +0 -4
  51. package/server/calculator/parser.ts +0 -137
  52. package/server/calculator/provider.ts +0 -10
  53. package/server/controllers/index.ts +0 -2
  54. package/server/controllers/kpi-metric-value-provider.ts +0 -79
  55. package/server/controllers/kpi-value-provider.ts +0 -51
  56. package/server/index.ts +0 -6
  57. package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
  58. package/server/migrations/1752190849681-seed-kpi.ts +0 -356
  59. package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
  60. package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
  61. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
  62. package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
  63. package/server/migrations/grade-data/x11-performance-table.json +0 -962
  64. package/server/migrations/grade-data/x12-performance-table.json +0 -611
  65. package/server/migrations/grade-data/x14-performance-table.json +0 -42
  66. package/server/migrations/grade-data/x21-performance-table.json +0 -889
  67. package/server/migrations/grade-data/x22-performance-table.json +0 -1064
  68. package/server/migrations/grade-data/x23-performance-table.json +0 -42
  69. package/server/migrations/grade-data/x31-performance-table.json +0 -644
  70. package/server/migrations/grade-data/x32-performance-table.json +0 -993
  71. package/server/migrations/grade-data/x33-performance-table.json +0 -195
  72. package/server/migrations/grade-data/x34-performance-table.json +0 -12
  73. package/server/migrations/grade-data/x35-performance-table.json +0 -42
  74. package/server/migrations/grade-data/x41-performance-table.json +0 -825
  75. package/server/migrations/grade-data/x42-performance-table.json +0 -786
  76. package/server/migrations/grade-data/x43-performance-table.json +0 -12
  77. package/server/migrations/grade-data/x44-performance-table.json +0 -42
  78. package/server/migrations/grade-data/x51-performance-table.json +0 -924
  79. package/server/migrations/grade-data/x52-performance-table.json +0 -42
  80. package/server/migrations/grade-data/x61-performance-table.json +0 -261
  81. package/server/migrations/grade-data/x62-performance-table.json +0 -42
  82. package/server/migrations/index.ts +0 -9
  83. package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
  84. package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
  85. package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
  86. package/server/migrations/seed-data/kpi-values-seed.json +0 -402
  87. package/server/migrations/seed-data/kpis-seed.json +0 -488
  88. package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
  89. package/server/routes.ts +0 -81
  90. package/server/service/index.ts +0 -51
  91. package/server/service/kpi/aggregate-kpi.ts +0 -103
  92. package/server/service/kpi/event-subscriber.ts +0 -29
  93. package/server/service/kpi/index.ts +0 -9
  94. package/server/service/kpi/kpi-formula.service.ts +0 -164
  95. package/server/service/kpi/kpi-grade.types.ts +0 -28
  96. package/server/service/kpi/kpi-history.ts +0 -126
  97. package/server/service/kpi/kpi-mutation.ts +0 -553
  98. package/server/service/kpi/kpi-query.ts +0 -224
  99. package/server/service/kpi/kpi-type.ts +0 -151
  100. package/server/service/kpi/kpi.ts +0 -254
  101. package/server/service/kpi-alert/index.ts +0 -3
  102. package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
  103. package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
  104. package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
  105. package/server/service/kpi-metric/index.ts +0 -7
  106. package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
  107. package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
  108. package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
  109. package/server/service/kpi-metric/kpi-metric.ts +0 -134
  110. package/server/service/kpi-metric-value/index.ts +0 -7
  111. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
  112. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
  113. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
  114. package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
  115. package/server/service/kpi-org-scope/index.ts +0 -6
  116. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
  117. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
  118. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
  119. package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
  120. package/server/service/kpi-scope/index.ts +0 -11
  121. package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
  122. package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
  123. package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
  124. package/server/service/kpi-scope/kpi-scope.ts +0 -143
  125. package/server/service/kpi-statistic/index.ts +0 -7
  126. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
  127. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
  128. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
  129. package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
  130. package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
  131. package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
  132. package/server/service/kpi-value/index.ts +0 -7
  133. package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
  134. package/server/service/kpi-value/kpi-value-query.ts +0 -61
  135. package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
  136. package/server/service/kpi-value/kpi-value-type.ts +0 -122
  137. package/server/service/kpi-value/kpi-value.ts +0 -160
  138. package/server/service/utils/value-date-util.ts +0 -119
  139. package/server/tsconfig.json +0 -10
  140. package/server/types/global.d.ts +0 -8
@@ -1,134 +0,0 @@
1
- import {
2
- CreateDateColumn,
3
- UpdateDateColumn,
4
- DeleteDateColumn,
5
- Entity,
6
- Index,
7
- Column,
8
- RelationId,
9
- ManyToOne,
10
- PrimaryGeneratedColumn
11
- } from 'typeorm'
12
- import { ObjectType, Field, Int, ID, registerEnumType } from 'type-graphql'
13
-
14
- import { Domain } from '@things-factory/shell'
15
- import { User } from '@things-factory/auth-base'
16
- import { DataSet } from '@things-factory/dataset'
17
- import { KpiPeriodType } from '../kpi/kpi'
18
-
19
- export enum KpiMetricCollectType {
20
- AUTO = 'AUTO', // 데이터셋 등 자동 수집
21
- MANUAL = 'MANUAL', // 수동 입력
22
- IMPORT = 'IMPORT', // 외부 파일 등 임포트
23
- EXTERNAL = 'EXTERNAL' // 외부 API 등
24
- }
25
- registerEnumType(KpiMetricCollectType, {
26
- name: 'KpiMetricCollectType',
27
- description: '방식: AUTO(자동), MANUAL(수동), IMPORT(임포트), EXTERNAL(외부API)'
28
- })
29
-
30
- @Entity()
31
- @Index('ix_kpi_metric_0', (kpiMetric: KpiMetric) => [kpiMetric.domain, kpiMetric.name], {
32
- where: '"deleted_at" IS NULL',
33
- unique: true
34
- })
35
- @ObjectType({ description: 'KPI metric entity. Represents a source data item used in KPI formulas.' })
36
- export class KpiMetric {
37
- @PrimaryGeneratedColumn('uuid')
38
- @Field(type => ID, { description: 'Unique identifier for this KPI metric.' })
39
- readonly id: string
40
-
41
- @ManyToOne(type => Domain)
42
- @Field({ nullable: true, description: 'Domain (tenant) to which this KPI metric belongs.' })
43
- domain?: Domain
44
-
45
- @RelationId((metric: KpiMetric) => metric.domain)
46
- @Field({ nullable: true, description: 'ID of the domain (tenant) for this KPI metric.' })
47
- domainId?: string
48
-
49
- @Column()
50
- @Field({ description: 'Metric code, unique within the domain, used in KPI formulas.' })
51
- name: string
52
-
53
- @Column({ nullable: true })
54
- @Field({ nullable: true, description: 'User-friendly name or description of the metric.' })
55
- description?: string
56
-
57
- @Column({ nullable: true })
58
- @Field({ nullable: true, description: 'Unit of measurement for this metric (e.g., %, count, hours).' })
59
- unit?: string
60
-
61
- @Column({ nullable: true })
62
- @Field({ nullable: true, description: 'Source of the metric data (e.g., system, method, dataset).' })
63
- source?: string
64
-
65
- @ManyToOne(type => DataSet, { nullable: true })
66
- @Field(type => DataSet, { nullable: true, description: 'Reference to the source dataset for this metric.' })
67
- dataSet?: DataSet
68
-
69
- @RelationId((metric: KpiMetric) => metric.dataSet)
70
- @Field({ nullable: true, description: 'ID of the referenced source dataset.' })
71
- dataSetId?: string
72
-
73
- @Column({ nullable: true })
74
- @Field({ nullable: true, description: 'Name of the field in the dataset this metric maps to.' })
75
- fieldName?: string
76
-
77
- @Column({ nullable: false, default: false })
78
- @Field({ nullable: true, description: 'Indicates whether this metric is active and usable.' })
79
- active?: boolean
80
-
81
- @Column({ nullable: true })
82
- @Field({
83
- nullable: true,
84
- description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
85
- })
86
- schedule?: string
87
-
88
- @Column({ nullable: true })
89
- @Field({ nullable: true, description: 'Schedule ID for the registered cron job.' })
90
- scheduleId?: string
91
-
92
- @Column({ nullable: true })
93
- @Field({ nullable: true, description: 'Timezone for the schedule.' })
94
- timezone?: string
95
-
96
- @Column({ type: 'enum', enum: KpiMetricCollectType, default: KpiMetricCollectType.AUTO })
97
- @Field(type => KpiMetricCollectType, { description: '데이터 수집 방식' })
98
- collectType: KpiMetricCollectType
99
-
100
- @Column({ default: 'DAY' })
101
- @Field(type => KpiPeriodType, { description: 'Aggregation period type for this metric.' })
102
- periodType: KpiPeriodType
103
-
104
- @CreateDateColumn()
105
- @Field({ nullable: true, description: 'Timestamp when this KPI metric was created.' })
106
- createdAt?: Date
107
-
108
- @UpdateDateColumn()
109
- @Field({ nullable: true, description: 'Timestamp when this KPI metric was last updated.' })
110
- updatedAt?: Date
111
-
112
- @DeleteDateColumn()
113
- @Field({ nullable: true, description: 'Timestamp when this KPI metric was deleted (soft delete).' })
114
- deletedAt?: Date
115
-
116
- @ManyToOne(type => User, { nullable: true })
117
- @Field(type => User, { nullable: true, description: 'User who created this KPI metric.' })
118
- creator?: User
119
-
120
- @RelationId((metric: KpiMetric) => metric.creator)
121
- @Field({ nullable: true, description: 'ID of the user who created this KPI metric.' })
122
- creatorId?: string
123
-
124
- @ManyToOne(type => User, { nullable: true })
125
- @Field(type => User, { nullable: true, description: 'User who last updated this KPI metric.' })
126
- updater?: User
127
-
128
- @RelationId((metric: KpiMetric) => metric.updater)
129
- @Field({ nullable: true, description: 'ID of the user who last updated this KPI metric.' })
130
- updaterId?: string
131
-
132
- @Field(type => String, { nullable: true, description: 'Thumbnail image or file path for this KPI metric.' })
133
- thumbnail?: string
134
- }
@@ -1,7 +0,0 @@
1
- import { KpiMetricValue } from './kpi-metric-value'
2
- import { KpiMetricValueQuery } from './kpi-metric-value-query'
3
- import { KpiMetricValueMutation } from './kpi-metric-value-mutation'
4
-
5
- export const entities = [KpiMetricValue]
6
- export const resolvers = [KpiMetricValueQuery, KpiMetricValueMutation]
7
- export const subscribers = []
@@ -1,270 +0,0 @@
1
- import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
2
- import { In } from 'typeorm'
3
- import { getRepository } from '@things-factory/shell'
4
- import { Float } from 'type-graphql'
5
- import { KpiPeriodType } from '../kpi/kpi'
6
-
7
- import { KpiMetricValue } from './kpi-metric-value'
8
- import { NewKpiMetricValue, KpiMetricValuePatch } from './kpi-metric-value-type'
9
- import { KpiMetric } from '../kpi-metric/kpi-metric'
10
- import { ScalarObject } from '@things-factory/shell'
11
- import { getDefaultValueDate } from '../utils/value-date-util'
12
-
13
- @Resolver(KpiMetricValue)
14
- export class KpiMetricValueMutation {
15
- @Directive('@transaction')
16
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
17
- @Mutation(returns => KpiMetricValue, { description: 'Create a new metric value with the provided details.' })
18
- async createKpiMetricValue(
19
- @Arg('metricValue', { description: 'Input object containing details for the new metric value.' })
20
- metricValue: NewKpiMetricValue,
21
- @Ctx() context: ResolverContext
22
- ): Promise<KpiMetricValue> {
23
- const { domain, user, tx } = context.state
24
-
25
- let metric = metricValue.metricId
26
- ? await getRepository(KpiMetric).findOne({ where: { id: metricValue.metricId } })
27
- : undefined
28
-
29
- // valueDate 자동 생성/보정 로직 추가
30
- let periodType = metricValue.periodType
31
- if (!periodType && metric) {
32
- periodType = metric.periodType
33
- }
34
- if (!periodType) {
35
- periodType = KpiPeriodType.DAY
36
- }
37
- // periodType을 string으로 변환 후 KpiPeriodType으로 강제 변환
38
- periodType = KpiPeriodType[String(periodType) as keyof typeof KpiPeriodType]
39
- let valueDate = metricValue.valueDate
40
- if (!valueDate || !isValueDateValidForPeriodType(valueDate, periodType)) {
41
- valueDate = getDefaultValueDate(periodType)
42
- }
43
-
44
- const entity: Partial<KpiMetricValue> = {
45
- metric,
46
- metricId: metricValue.metricId,
47
- valueDate,
48
- periodType,
49
- value: metricValue.value,
50
- org: metricValue.org,
51
- unit: metricValue.unit,
52
- meta: metricValue.meta,
53
- domain: domain,
54
- creator: user,
55
- updater: user
56
- }
57
-
58
- return await getRepository(KpiMetricValue, tx).save(entity)
59
- }
60
-
61
- @Directive('@transaction')
62
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
63
- @Mutation(returns => KpiMetricValue, { description: 'Record a metric value by metric name, value, meta, and org.' })
64
- async recordKpiMetricValue(
65
- @Arg('metricName', { description: 'Metric code/name.' }) metricName: string,
66
- @Arg('value', type => Float, { nullable: true, description: 'Metric value (number).' }) value: number | null,
67
- @Arg('meta', type => ScalarObject, { nullable: true, description: 'Extended or non-numeric information (JSON).' })
68
- meta: any,
69
- @Arg('org', { nullable: true, description: 'Organizational unit for this value (project, department, company, employee, etc.)' })
70
- org: string | null,
71
- @Ctx() context: ResolverContext
72
- ): Promise<KpiMetricValue> {
73
- const { domain, user, tx } = context.state
74
- const metric = await getRepository(KpiMetric).findOne({ where: { name: metricName, domain: { id: domain.id } } })
75
- if (!metric) throw new Error(`Metric not found: ${metricName}`)
76
- const periodType = metric.periodType || KpiPeriodType.DAY
77
- const valueDate = getDefaultValueDate(periodType)
78
- if (value == null && meta == null) {
79
- throw new Error('Either value or meta must be provided.')
80
- }
81
- const repo = getRepository(KpiMetricValue, tx)
82
- let entity = await repo.findOne({
83
- where: {
84
- metric: { id: metric.id },
85
- valueDate,
86
- periodType,
87
- org,
88
- domain: { id: domain.id }
89
- }
90
- })
91
- if (entity) {
92
- entity.value = value
93
- entity.meta = meta
94
- entity.updater = user
95
- } else {
96
- entity = repo.create({
97
- metric,
98
- metricId: metric.id,
99
- value,
100
- meta,
101
- valueDate,
102
- periodType,
103
- org,
104
- domain,
105
- creator: user,
106
- updater: user
107
- })
108
- }
109
- return await repo.save(entity)
110
- }
111
-
112
- @Directive('@transaction')
113
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
114
- @Mutation(returns => KpiMetricValue, { description: 'To modify KpiMetricValue information' })
115
- async updateKpiMetricValue(
116
- @Arg('id') id: string,
117
- @Arg('patch') patch: KpiMetricValuePatch,
118
- @Ctx() context: ResolverContext
119
- ): Promise<KpiMetricValue> {
120
- const { domain, user, tx } = context.state
121
-
122
- const repository = getRepository(KpiMetricValue, tx)
123
- const metricValue = await repository.findOne({
124
- where: { domain: { id: domain.id }, id }
125
- })
126
-
127
- let metric = patch.metricId
128
- ? await getRepository(KpiMetric).findOne({ where: { id: patch.metricId } })
129
- : metricValue.metric
130
-
131
- const entity: any = {
132
- ...metricValue,
133
- ...patch,
134
- metric,
135
- updater: user
136
- }
137
-
138
- return await repository.save(entity)
139
- }
140
-
141
- @Directive('@transaction')
142
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
143
- @Mutation(returns => [KpiMetricValue], { description: "To modify multiple KpiMetricValues' information" })
144
- async updateMultipleKpiMetricValue(
145
- @Arg('patches', type => [KpiMetricValuePatch]) patches: KpiMetricValuePatch[],
146
- @Ctx() context: ResolverContext
147
- ): Promise<KpiMetricValue[]> {
148
- const { domain, user, tx } = context.state
149
-
150
- let results = []
151
- const _createRecords = patches.filter((patch: any) => patch.cuFlag && patch.cuFlag.toUpperCase() === '+')
152
- const _updateRecords = patches.filter((patch: any) => patch.cuFlag && patch.cuFlag.toUpperCase() === 'M')
153
- const metricValueRepo = getRepository(KpiMetricValue, tx)
154
-
155
- if (_createRecords.length > 0) {
156
- for (let i = 0; i < _createRecords.length; i++) {
157
- const newRecord = _createRecords[i]
158
-
159
- if (!newRecord.metricId) {
160
- throw new Error('metricId is required for creating new metric value')
161
- }
162
-
163
- const metric = await getRepository(KpiMetric).findOne({
164
- where: { domain: { id: domain.id }, id: newRecord.metricId }
165
- })
166
-
167
- if (!metric) {
168
- throw new Error(`Metric not found: ${newRecord.metricId}`)
169
- }
170
-
171
- const result = await metricValueRepo.save({
172
- ...newRecord,
173
- metric,
174
- domain,
175
- creator: user,
176
- updater: user
177
- } as any)
178
- results.push({ ...result, cuFlag: '+' })
179
- }
180
- }
181
-
182
- if (_updateRecords.length > 0) {
183
- for (let i = 0; i < _updateRecords.length; i++) {
184
- const updateRecord = _updateRecords[i]
185
- const metricValue = await metricValueRepo.findOneBy({ id: updateRecord.id })
186
-
187
- if (!updateRecord.metricId) {
188
- throw new Error('metricId is required for updating value')
189
- }
190
-
191
- const metric = await getRepository(KpiMetric).findOne({
192
- where: { domain: { id: domain.id }, id: updateRecord.metricId }
193
- })
194
-
195
- if (!metric) {
196
- throw new Error(`Metric not found: ${updateRecord.metricId}`)
197
- }
198
-
199
- const result = await metricValueRepo.save({
200
- ...metricValue,
201
- ...updateRecord,
202
- metric,
203
- updater: user
204
- } as any)
205
- results.push({ ...result, cuFlag: 'M' })
206
- }
207
- }
208
-
209
- return results
210
- }
211
-
212
- @Directive('@transaction')
213
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
214
- @Mutation(returns => Boolean, { description: 'To delete KpiMetricValue' })
215
- async deleteKpiMetricValue(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
216
- const { domain, tx } = context.state
217
- await getRepository(KpiMetricValue, tx).delete({ domain: { id: domain.id }, id })
218
- return true
219
- }
220
-
221
- @Directive('@transaction')
222
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
223
- @Mutation(returns => Boolean, { description: 'To delete multiple KpiMetricValues' })
224
- async deleteKpiMetricValues(
225
- @Arg('ids', type => [String]) ids: string[],
226
- @Ctx() context: ResolverContext
227
- ): Promise<boolean> {
228
- const { domain, tx } = context.state
229
- await getRepository(KpiMetricValue, tx).delete({
230
- domain: { id: domain.id },
231
- id: In(ids)
232
- })
233
- return true
234
- }
235
-
236
- @Directive('@transaction')
237
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
238
- @Mutation(returns => Boolean, { description: 'To import multiple KpiMetricValues' })
239
- async importKpiMetricValues(
240
- @Arg('metricValues', type => [KpiMetricValuePatch]) metricValues: KpiMetricValuePatch[],
241
- @Ctx() context: ResolverContext
242
- ): Promise<boolean> {
243
- const { domain, tx } = context.state
244
- await Promise.all(
245
- metricValues.map(async (metricValue: KpiMetricValuePatch) => {
246
- await getRepository(KpiMetricValue, tx).save({ domain, ...metricValue } as any)
247
- })
248
- )
249
- return true
250
- }
251
- }
252
-
253
- // valueDate와 periodType의 일치 여부를 검사하는 유틸리티 함수(임시, 아래에 추가)
254
- function isValueDateValidForPeriodType(valueDate: string, periodType: KpiPeriodType): boolean {
255
- if (!valueDate) return false
256
- switch (periodType) {
257
- case KpiPeriodType.DAY:
258
- return /^\d{4}-\d{2}-\d{2}$/.test(valueDate)
259
- case KpiPeriodType.MONTH:
260
- return /^\d{4}-\d{2}$/.test(valueDate)
261
- case KpiPeriodType.QUARTER:
262
- return /^\d{4}-Q[1-4]$/.test(valueDate)
263
- case KpiPeriodType.WEEK:
264
- return /^\d{4}-W\d{1,2}$/.test(valueDate)
265
- case KpiPeriodType.ALLTIME:
266
- return valueDate === 'ALLTIME'
267
- default:
268
- return true
269
- }
270
- }
@@ -1,62 +0,0 @@
1
- import { Directive, Resolver, Query, Args, Arg, Ctx, FieldResolver, Root } from 'type-graphql'
2
- import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
3
- import { User } from '@things-factory/auth-base'
4
- import { KpiMetricValue } from './kpi-metric-value'
5
- import { KpiMetricValueList } from './kpi-metric-value-type'
6
- import { KpiMetric } from '../kpi-metric/kpi-metric'
7
-
8
- @Resolver(KpiMetricValue)
9
- export class KpiMetricValueQuery {
10
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
11
- @Query(returns => KpiMetricValueList, { description: 'To fetch multiple KpiMetricValues' })
12
- async kpiMetricValues(
13
- @Args(type => ListParam) params: ListParam,
14
- @Ctx() context: ResolverContext
15
- ): Promise<KpiMetricValueList> {
16
- const { domain } = context.state
17
- const queryBuilder = getQueryBuilderFromListParams({
18
- domain,
19
- params,
20
- repository: await getRepository(KpiMetricValue),
21
- searchables: ['metricId', 'group', 'valueDate']
22
- })
23
- const [items, total] = await queryBuilder.getManyAndCount()
24
- return { items, total }
25
- }
26
-
27
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
28
- @Query(returns => KpiMetricValue, {
29
- nullable: true,
30
- description: 'Fetch a single metric value by its unique identifier.'
31
- })
32
- async kpiMetricValue(
33
- @Arg('id', { description: 'Unique identifier of the metric value to fetch.' }) id: string,
34
- @Ctx() context: ResolverContext
35
- ): Promise<KpiMetricValue | null> {
36
- const { domain } = context.state
37
- return await getRepository(KpiMetricValue).findOne({
38
- where: { domain: { id: domain.id }, id }
39
- })
40
- }
41
-
42
- @FieldResolver(type => Domain)
43
- async domain(@Root() metricValue: KpiMetricValue): Promise<Domain> {
44
- return metricValue.domainId && (await getRepository(Domain).findOneBy({ id: metricValue.domainId }))
45
- }
46
-
47
- @FieldResolver(type => User)
48
- async updater(@Root() metricValue: KpiMetricValue): Promise<User> {
49
- return metricValue.updaterId && (await getRepository(User).findOneBy({ id: metricValue.updaterId }))
50
- }
51
-
52
- @FieldResolver(type => User)
53
- async creator(@Root() metricValue: KpiMetricValue): Promise<User> {
54
- return metricValue.creatorId && (await getRepository(User).findOneBy({ id: metricValue.creatorId }))
55
- }
56
-
57
- @FieldResolver(type => KpiMetric)
58
- async metric(@Root() metricValue: KpiMetricValue): Promise<KpiMetric | null> {
59
- if (!metricValue.metricId) return null
60
- return await getRepository(KpiMetric).findOneBy({ id: metricValue.metricId })
61
- }
62
- }
@@ -1,82 +0,0 @@
1
- import { ObjectType, Field, ID, Float, InputType, Int } from 'type-graphql'
2
- import { ScalarObject } from '@things-factory/shell'
3
- import { KpiMetricValue } from './kpi-metric-value'
4
- import { KpiPeriodType } from '../kpi/kpi'
5
-
6
- @InputType({
7
- description: 'Input type for creating a new metric value. Used in mutations to provide metric value details.'
8
- })
9
- export class NewKpiMetricValue {
10
- @Field(type => ID, { description: 'ID of the metric to which this value belongs.' })
11
- metricId: string
12
-
13
- @Field({ description: 'Date or period for which this metric value is recorded (e.g., day, month, quarter, range).' })
14
- valueDate: string
15
-
16
- @Field(type => KpiPeriodType, { description: 'Aggregation period type for this metric value.' })
17
- periodType: KpiPeriodType
18
-
19
- @Field(type => Float, { description: 'The value for this metric and period.' })
20
- value: number
21
-
22
- @Field({ nullable: true, description: 'Unit of measurement for this metric value.' })
23
- unit?: string
24
-
25
- @Field(type => ScalarObject, {
26
- nullable: true,
27
- description:
28
- 'Extended or non-numeric information related to this metric value, stored as JSON. Can include status, comments, or other metadata.'
29
- })
30
- meta?: any
31
-
32
- @Field({ nullable: true, description: 'Organizational unit for this value (project, department, company, employee, etc.)' })
33
- org?: string
34
- }
35
-
36
- @InputType({
37
- description: 'Input type for updating an existing metric value. Used in mutations to patch metric value details.'
38
- })
39
- export class KpiMetricValuePatch {
40
- @Field(type => ID, { nullable: true, description: 'ID of the metric value to update.' })
41
- id?: string
42
-
43
- @Field(type => ID, { nullable: true, description: 'ID of the metric to which this value belongs.' })
44
- metricId?: string
45
-
46
- @Field({
47
- nullable: true,
48
- description: 'Date or period for which this metric value is recorded (e.g., day, month, quarter, range).'
49
- })
50
- valueDate?: string
51
-
52
- @Field(type => KpiPeriodType, { nullable: true, description: 'Aggregation period type for this metric value.' })
53
- periodType?: KpiPeriodType
54
-
55
- @Field(type => Float, { nullable: true, description: 'The value for this metric and period.' })
56
- value?: number
57
-
58
- @Field({ nullable: true, description: 'Unit of measurement for this metric value.' })
59
- unit?: string
60
-
61
- @Field(type => ScalarObject, {
62
- nullable: true,
63
- description:
64
- 'Extended or non-numeric information related to this metric value, stored as JSON. Can include status, comments, or other metadata.'
65
- })
66
- meta?: any
67
-
68
- @Field({ nullable: true, description: 'Organizational unit for this value (project, department, company, employee, etc.)' })
69
- org?: string
70
-
71
- @Field({ nullable: true, description: 'Custom flag for update operations (internal use).' })
72
- cuFlag?: string
73
- }
74
-
75
- @ObjectType()
76
- export class KpiMetricValueList {
77
- @Field(type => [KpiMetricValue])
78
- items: KpiMetricValue[]
79
-
80
- @Field(type => Int)
81
- total: number
82
- }
@@ -1,93 +0,0 @@
1
- import {
2
- Entity,
3
- PrimaryGeneratedColumn,
4
- Column,
5
- ManyToOne,
6
- Index,
7
- JoinColumn,
8
- CreateDateColumn,
9
- UpdateDateColumn,
10
- RelationId
11
- } from 'typeorm'
12
- import { ObjectType, Field, ID, Float } from 'type-graphql'
13
- import { Domain, ScalarObject } from '@things-factory/shell'
14
- import { KpiMetric } from '../kpi-metric/kpi-metric'
15
- import { KpiPeriodType } from '../kpi/kpi'
16
- import { User } from '@things-factory/auth-base'
17
-
18
- @Entity()
19
- @Index('ix_kpi_metric_value_latest', ['domain', 'metric', 'valueDate', 'org'], { unique: true })
20
- @ObjectType({ description: 'Current value for each KPI metric (can be used for both state and history).' })
21
- export class KpiMetricValue {
22
- @PrimaryGeneratedColumn('uuid')
23
- @Field(type => ID)
24
- id: string
25
-
26
- @ManyToOne(() => Domain)
27
- @Field(type => Domain, { nullable: true })
28
- domain?: Domain
29
-
30
- @RelationId((entity: KpiMetricValue) => entity.domain)
31
- @Field({ nullable: true })
32
- domainId?: string
33
-
34
- @ManyToOne(() => KpiMetric, { nullable: false, onDelete: 'CASCADE' })
35
- @JoinColumn({ name: 'metricId' })
36
- @Field(type => KpiMetric)
37
- metric: KpiMetric
38
-
39
- @RelationId((entity: KpiMetricValue) => entity.metric)
40
- @Field()
41
- metricId: string
42
-
43
- @Column('float')
44
- @Field(type => Float)
45
- value: number
46
-
47
- @Column({ nullable: true })
48
- @Field({ nullable: true })
49
- unit?: string
50
-
51
- @Column()
52
- @Field({
53
- description:
54
- 'Date or period for which this metric value is recorded (e.g., day: YYYY-MM-DD, month: YYYY-MM, range: YYYY-MM-DD~YYYY-MM-DD).'
55
- })
56
- valueDate: string
57
-
58
- @Column({ default: 'DAY' })
59
- @Field(type => KpiPeriodType)
60
- periodType: KpiPeriodType
61
-
62
- @Column({ default: '' })
63
- @Field({ nullable: true, description: 'Organizational unit for this value (project, department, company, employee, etc.)' })
64
- org?: string
65
-
66
- @Column({ type: 'simple-json', nullable: true })
67
- @Field(type => ScalarObject, { nullable: true })
68
- meta?: any
69
-
70
- @CreateDateColumn()
71
- @Field({ nullable: true })
72
- createdAt?: Date
73
-
74
- @UpdateDateColumn()
75
- @Field({ nullable: true })
76
- updatedAt?: Date
77
-
78
- @ManyToOne(() => User, { nullable: true })
79
- @Field(type => User, { nullable: true })
80
- creator?: User
81
-
82
- @RelationId((entity: KpiMetricValue) => entity.creator)
83
- @Field({ nullable: true })
84
- creatorId?: string
85
-
86
- @ManyToOne(() => User, { nullable: true })
87
- @Field(type => User, { nullable: true })
88
- updater?: User
89
-
90
- @RelationId((entity: KpiMetricValue) => entity.updater)
91
- @Field({ nullable: true })
92
- updaterId?: string
93
- }
@@ -1,6 +0,0 @@
1
- import { KpiOrgScope } from './kpi-org-scope'
2
- import { KpiOrgScopeQuery } from './kpi-org-scope-query'
3
- import { KpiOrgScopeMutation } from './kpi-org-scope-mutation'
4
-
5
- export const entities = [KpiOrgScope]
6
- export const resolvers = [KpiOrgScopeQuery, KpiOrgScopeMutation]