@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,51 +0,0 @@
1
- /* EXPORT ENTITY TYPES */
2
- export * from './kpi-statistic/kpi-statistic'
3
- export * from './kpi/kpi'
4
- export * from './kpi/kpi-type'
5
- export * from './kpi-value/kpi-value'
6
- export * from './kpi-value/kpi-value-type'
7
- export * from './kpi-metric/kpi-metric'
8
- export * from './kpi-metric/kpi-metric-type'
9
- export * from './kpi-metric-value/kpi-metric-value'
10
- export * from './kpi-metric-value/kpi-metric-value-type'
11
- export * from './kpi-scope/kpi-scope'
12
- export * from './kpi-org-scope/kpi-org-scope'
13
-
14
- /* IMPORT ENTITIES AND RESOLVERS */
15
- import {
16
- entities as KpiStatisticEntities,
17
- resolvers as KpiStatisticResolvers,
18
- subscribers as KpiStatisticSubscribers
19
- } from './kpi-statistic'
20
- import { entities as KpiEntities, resolvers as KpiResolvers } from './kpi'
21
- import { entities as KpiValueEntities, resolvers as KpiValueResolvers } from './kpi-value'
22
- import { entities as KpiMetricEntities, resolvers as KpiMetricResolvers } from './kpi-metric'
23
- import { entities as KpiMetricValueEntities, resolvers as KpiMetricValueResolvers } from './kpi-metric-value'
24
- import { entities as KpiOrgScopeEntities, resolvers as KpiOrgScopeResolvers } from './kpi-org-scope'
25
- import { resolvers as KpiAlertResolvers } from './kpi-alert'
26
- import { entities as KpiScopeEntities, resolvers as KpiScopeResolvers } from './kpi-scope'
27
-
28
- export const entities = [
29
- /* ENTITIES */
30
- ...KpiStatisticEntities,
31
- ...KpiEntities,
32
- ...KpiValueEntities,
33
- ...KpiMetricEntities,
34
- ...KpiMetricValueEntities,
35
- ...KpiOrgScopeEntities,
36
- ...KpiScopeEntities
37
- ]
38
-
39
- export const schema = {
40
- resolverClasses: [
41
- /* RESOLVER CLASSES */
42
- ...KpiStatisticResolvers,
43
- ...KpiResolvers,
44
- ...KpiValueResolvers,
45
- ...KpiMetricResolvers,
46
- ...KpiMetricValueResolvers,
47
- ...KpiOrgScopeResolvers,
48
- ...KpiAlertResolvers,
49
- ...KpiScopeResolvers
50
- ]
51
- }
@@ -1,103 +0,0 @@
1
- import { getRepository } from '@things-factory/shell'
2
- import { Kpi } from './kpi'
3
- import { KpiMetric } from '../kpi-metric/kpi-metric'
4
- import { KpiValue, KpiValueInputType } from '../kpi-value/kpi-value'
5
- import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
6
- import { KpiFormulaService } from './kpi-formula.service'
7
- import { aggregateKpiMetricValue } from '../kpi-metric/aggregate-kpi-metric'
8
- import { KpiValueScoreService } from '../kpi-value/kpi-value-score.service'
9
-
10
- /**
11
- * KPI 단위 집계/산식 자동화 함수
12
- * @param kpiId KPI ID
13
- * @param domainId 도메인 ID
14
- * @param context ResolverContext
15
- * @returns 저장된 KPI Value 배열
16
- */
17
- export async function aggregateKpiValue(kpiId: string, domainId: string, context: ResolverContext) {
18
- const tx = context.state?.tx || getRepository(Kpi).manager
19
- const scoreService = new KpiValueScoreService()
20
-
21
- // 1. KPI 정보 조회
22
- const kpi = await getRepository(Kpi).findOne({ where: { id: kpiId, domain: { id: domainId } } })
23
- if (!kpi) throw new Error('KPI 정보 없음')
24
- if (!kpi.active) throw new Error('비활성화된 KPI')
25
- if (!kpi.formula) throw new Error('KPI formula 없음')
26
-
27
- // 2. formula 파싱 및 metric code별 값 집계
28
- const formulaService = new KpiFormulaService()
29
- const metricCodes = formulaService.extractMetricCodes(kpi.formula)
30
- const codeValueMap: Record<string, any[]> = {}
31
- for (const code of metricCodes) {
32
- // code로 metric 찾기
33
- const metric = await getRepository(KpiMetric).findOne({ where: { name: code, domain: { id: domainId } } })
34
- if (!metric) throw new Error(`KPI formula metric '${code}' not found`)
35
- codeValueMap[code] = await aggregateKpiMetricValue(metric.id, domainId, context)
36
- }
37
- // org/date/period별로 값 매핑(가장 최근 기준, org key 조합)
38
- const orgKey = v => [v.date, v.period, v.org].join('|')
39
- const orgMap: Record<string, any> = {}
40
- for (const code of metricCodes) {
41
- for (const v of codeValueMap[code]) {
42
- const key = orgKey(v)
43
- orgMap[key] = orgMap[key] || { ...v, _values: {} }
44
- orgMap[key]._values[code] = v.value
45
- }
46
- }
47
- // formula 계산 (js eval)
48
- const savedValues = []
49
- for (const key in orgMap) {
50
- const ctx = orgMap[key]._values
51
- let value = null
52
- try {
53
- value = Function(...Object.keys(ctx), `return (${kpi.formula})`)(...Object.values(ctx))
54
- } catch (e) {
55
- value = null
56
- }
57
- const valueDate = orgMap[key].date
58
- const org = orgMap[key].org
59
- const version = kpi.version || 1
60
- if (value == null || isNaN(value)) continue
61
- // KpiOrgScope 처리 (org 문자열로 찾기 또는 생성)
62
- const kpiOrgScopeRepo = getRepository(KpiOrgScope, context.state?.tx)
63
- let kpiOrgScope = await kpiOrgScopeRepo.findOne({
64
- where: { org: org, domain: { id: domainId } }
65
- })
66
- if (!kpiOrgScope) {
67
- // 새 KpiOrgScope 생성
68
- kpiOrgScope = await kpiOrgScopeRepo.save({
69
- entityType: 'AutoAggregate',
70
- entityId: `auto-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
71
- entityName: org,
72
- org: org,
73
- domain: kpi.domain
74
- })
75
- }
76
-
77
- // upsert(동일 KPI, valueDate, kpiOrgScope, version) 기준으로 저장
78
- const repo = getRepository(KpiValue, context.state?.tx)
79
- const existing = await repo.findOne({
80
- where: { kpi: { id: kpi.id }, valueDate, kpiOrgScope: { id: kpiOrgScope.id }, version, domain: { id: domainId } }
81
- })
82
- let entity = existing || repo.create()
83
- entity.kpi = kpi
84
- entity.kpiId = kpi.id
85
- entity.version = version
86
- entity.valueDate = valueDate
87
- entity.value = value
88
- entity.kpiOrgScope = kpiOrgScope
89
- entity.kpiOrgScopeId = kpiOrgScope.id
90
- entity.inputType = KpiValueInputType.AUTO
91
- entity.source = 'AUTO'
92
- entity.domain = kpi.domain
93
- entity.creator = context.state?.user
94
- entity.updater = context.state?.user
95
-
96
- // 성과 점수 자동 계산 및 저장
97
- await scoreService.calculateAndSaveScore(entity, kpi)
98
-
99
- entity = await repo.save(entity)
100
- savedValues.push(entity)
101
- }
102
- return savedValues
103
- }
@@ -1,29 +0,0 @@
1
- import { EventSubscriber } from 'typeorm'
2
-
3
- import { HistoryEntitySubscriber } from '@operato/typeorm-history'
4
-
5
- import { Kpi } from './kpi'
6
- import { KpiHistory } from './kpi-history'
7
-
8
- @EventSubscriber()
9
- export class KpiHistoryEntitySubscriber extends HistoryEntitySubscriber<Kpi, KpiHistory> {
10
- public get entity() {
11
- return Kpi
12
- }
13
-
14
- public get historyEntity() {
15
- return KpiHistory
16
- }
17
-
18
- public async afterInsert(event): Promise<void> {
19
- if (event.entity.state == 'RELEASE') {
20
- await super.afterInsert(event as any)
21
- }
22
- }
23
-
24
- public async afterUpdate(event): Promise<void> {
25
- if (event.entity.state == 'RELEASE') {
26
- await super.afterUpdate(event as any)
27
- }
28
- }
29
- }
@@ -1,9 +0,0 @@
1
- import { Kpi } from './kpi'
2
- import { KpiHistory } from './kpi-history'
3
- import { KpiHistoryEntitySubscriber } from './event-subscriber'
4
- import { KpiQuery } from './kpi-query'
5
- import { KpiMutation } from './kpi-mutation'
6
-
7
- export const entities = [Kpi, KpiHistory]
8
- export const resolvers = [KpiQuery, KpiMutation]
9
- export const subscribers = [KpiHistoryEntitySubscriber]
@@ -1,164 +0,0 @@
1
- import { getRepository } from '@things-factory/shell'
2
- import { KpiMetric } from '../kpi-metric/kpi-metric'
3
- import { Kpi } from './kpi'
4
- import { KpiValue } from '../kpi-value/kpi-value'
5
- import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
6
-
7
- /**
8
- * KPI formula 파싱/검증/매핑 유효성 검사 서비스
9
- * - formula에서 Metric 코드(변수명) 추출
10
- * - 각 Metric의 존재/매핑 유효성 검사
11
- * - 구문 오류, 미정의 Metric, 매핑 오류 등 에러 리포팅
12
- */
13
- export class KpiFormulaService {
14
- /**
15
- * formula 문자열에서 Metric 코드(변수명) 추출
16
- * @param formula KPI formula (예: 'defect_count / total_count * 100')
17
- * @returns string[] 추출된 Metric 코드 리스트
18
- */
19
- extractMetricCodes(formula: string): string[] {
20
- // 변수명: 알파벳/언더스코어로 시작, 숫자/알파벳/언더스코어 포함
21
- const regex = /[a-zA-Z_][a-zA-Z0-9_]*/g
22
- // 숫자/예약어/연산자 제외(후처리 필요)
23
- const reserved = new Set(['if', 'else', 'return', 'true', 'false'])
24
- return Array.from(new Set((formula.match(regex) || []).filter(code => isNaN(Number(code)) && !reserved.has(code))))
25
- }
26
-
27
- /**
28
- * formula 내 Metric 코드의 존재/매핑 유효성 검사 + 순환참조 검출
29
- * @param formula KPI formula
30
- * @param visited 순환참조 검출용 Set (재귀 호출 시 사용)
31
- * @returns { valid: boolean, errors: string[] }
32
- */
33
- async validateFormula(
34
- formula: string,
35
- visited: Set<string> = new Set()
36
- ): Promise<{ valid: boolean; errors: string[] }> {
37
- const errors: string[] = []
38
- const metricCodes = this.extractMetricCodes(formula)
39
- const metricRepo = getRepository(KpiMetric)
40
- for (const code of metricCodes) {
41
- if (visited.has(code)) {
42
- errors.push(`순환참조 감지: ${Array.from(visited).join(' → ')} → ${code}`)
43
- continue
44
- }
45
- visited.add(code)
46
- const metric = await metricRepo.findOne({ where: { name: code } })
47
- if (!metric) {
48
- errors.push(`Metric '${code}' is not defined.`)
49
- visited.delete(code)
50
- continue
51
- }
52
- if (!metric.dataSetId || !metric.fieldName) {
53
- errors.push(`Metric '${code}' is not mapped to a dataset field.`)
54
- }
55
- // metric이 formula를 가지고 있다면 재귀적으로 순환참조 검증
56
- if ((metric as any).formula) {
57
- const subResult = await this.validateFormula((metric as any).formula, new Set(visited))
58
- if (!subResult.valid) errors.push(...subResult.errors)
59
- }
60
- visited.delete(code)
61
- }
62
- // (선택) formula 구문 오류, 괄호 불일치 등 추가 검사 가능
63
- // ...
64
- return { valid: errors.length === 0, errors }
65
- }
66
-
67
- /**
68
- * 계층 구조 KPI의 값을 자식 KPI들로부터 집계 계산
69
- * @param kpiId 부모 KPI ID
70
- * @param valueDate 계산 대상 날짜
71
- * @param org 조직 단위
72
- * @returns Promise<number | null> 집계된 값
73
- */
74
- async calculateHierarchicalValue(kpiId: string, valueDate: string, org?: string): Promise<number | null> {
75
- const kpiRepo = getRepository(Kpi)
76
- const kpi = await kpiRepo.findOne({
77
- where: { id: kpiId },
78
- relations: ['children']
79
- })
80
-
81
- if (!kpi || !kpi.children || kpi.children.length === 0) {
82
- return null
83
- }
84
-
85
- const childValues: number[] = []
86
- const childWeights: number[] = []
87
-
88
- for (const child of kpi.children) {
89
- let childValue: number | null = null
90
-
91
- if (child.isLeaf) {
92
- // Leaf KPI인 경우 KpiValue에서 실제 값을 가져옴
93
- const kpiValueRepo = getRepository(KpiValue)
94
- let kpiValue: KpiValue | null = null
95
- if (org) {
96
- const kpiOrgScope = await getRepository(KpiOrgScope).findOne({
97
- where: { org: org, domain: { id: child.domain?.id } }
98
- })
99
- if (kpiOrgScope) {
100
- kpiValue = await kpiValueRepo.findOne({
101
- where: {
102
- kpi: { id: child.id },
103
- valueDate,
104
- kpiOrgScope: { id: kpiOrgScope.id }
105
- }
106
- })
107
- }
108
- } else {
109
- // org가 없는 경우 kpiOrgScope 없이 조회
110
- kpiValue = await kpiValueRepo.findOne({
111
- where: {
112
- kpi: { id: child.id },
113
- valueDate,
114
- kpiOrgScope: null
115
- }
116
- })
117
- }
118
- childValue = kpiValue?.value || null
119
- } else {
120
- // 중간 노드인 경우 재귀적으로 계산
121
- childValue = await this.calculateHierarchicalValue(child.id, valueDate, org)
122
- }
123
-
124
- if (childValue !== null) {
125
- childValues.push(childValue)
126
- childWeights.push(child.weight || 1)
127
- }
128
- }
129
-
130
- if (childValues.length === 0) {
131
- return null
132
- }
133
-
134
- // 가중 평균 계산
135
- const totalWeight = childWeights.reduce((sum, weight) => sum + weight, 0)
136
- const weightedSum = childValues.reduce((sum, value, index) => sum + value * childWeights[index], 0)
137
-
138
- return totalWeight > 0 ? weightedSum / totalWeight : null
139
- }
140
-
141
- /**
142
- * KPI가 순환 참조를 생성하는지 확인
143
- * @param kpiId KPI ID
144
- * @param parentId 설정하려는 부모 KPI ID
145
- * @returns Promise<boolean> 순환 참조 여부
146
- */
147
- async hasCircularReference(kpiId: string, parentId: string): Promise<boolean> {
148
- if (kpiId === parentId) {
149
- return true
150
- }
151
-
152
- const kpiRepo = getRepository(Kpi)
153
- const parent = await kpiRepo.findOne({
154
- where: { id: parentId },
155
- relations: ['parent']
156
- })
157
-
158
- if (!parent || !parent.parent) {
159
- return false
160
- }
161
-
162
- return await this.hasCircularReference(kpiId, parent.parent.id)
163
- }
164
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * KPI 성과 점수 lookup table 타입 정의
3
- * 복잡한 성과 점수 변환을 수식으로 표현하기 어려운 경우 사용
4
- */
5
- export interface KpiScore {
6
- /** 성과 지수명 (예: A, B, C, 우수, 양호 등) */
7
- name: string
8
-
9
- /** 성과 점수 변환 기준 최소값 */
10
- minValue: number
11
-
12
- /** 성과 점수 변환 기준 최대값 */
13
- maxValue: number
14
-
15
- /** 성과 지수 점수 (선택사항) */
16
- score?: number
17
-
18
- /** 성과 지수 색상 (선택사항) */
19
- color?: string
20
-
21
- /** 성과 지수 설명 (선택사항) */
22
- description?: string
23
- }
24
-
25
- /**
26
- * KPI 성과 점수 lookup table 배열 타입
27
- */
28
- export type KpiScores = KpiScore[]
@@ -1,126 +0,0 @@
1
- import { Field, ID, ObjectType } from 'type-graphql'
2
- import { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, JoinColumn } from 'typeorm'
3
-
4
- import {
5
- HistoryActionColumn,
6
- HistoryActionType,
7
- HistoryEntityInterface,
8
- HistoryOriginalIdColumn
9
- } from '@operato/typeorm-history'
10
- import { Role, User } from '@things-factory/auth-base'
11
- import { config } from '@things-factory/env'
12
- import { Domain, ScalarObject } from '@things-factory/shell'
13
-
14
- import { Kpi, KpiStatus, KpiPeriodType } from './kpi'
15
- import { KpiScores } from './kpi-grade.types'
16
-
17
- const ORMCONFIG = config.get('ormconfig', {})
18
- const DATABASE_TYPE = ORMCONFIG.type
19
-
20
- @Entity()
21
- @Index('ix_kpi_history_0', (kpiHistory: KpiHistory) => [kpiHistory.originalId, kpiHistory.version], { unique: true })
22
- @Index('ix_kpi_history_1', (kpiHistory: KpiHistory) => [kpiHistory.domain, kpiHistory.originalId, kpiHistory.version], {
23
- unique: true
24
- })
25
- @ObjectType({ description: 'History Entity of Kpi' })
26
- export class KpiHistory implements HistoryEntityInterface<Kpi> {
27
- @PrimaryGeneratedColumn('uuid')
28
- @Field(type => ID)
29
- readonly id: string
30
-
31
- @Column({ nullable: true, default: 1 })
32
- @Field({ nullable: true })
33
- version?: number = 1
34
-
35
- @ManyToOne(type => Domain)
36
- @Field({ nullable: true })
37
- domain?: Domain
38
-
39
- @RelationId((kpi: Kpi) => kpi.domain)
40
- domainId?: string
41
-
42
- @Column()
43
- @Field()
44
- name: string
45
-
46
- @Column({ nullable: true })
47
- @Field({ nullable: true })
48
- description?: string
49
-
50
- @Column({ nullable: true })
51
- @Field({ nullable: true, description: 'Calculation formula for the KPI.' })
52
- formula?: string
53
-
54
- @Column({ nullable: false, default: false })
55
- @Field({ nullable: true, description: 'Whether this KPI is active (usable) or not.' })
56
- active?: boolean
57
-
58
- @Column({ nullable: true })
59
- @Field({ nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
60
- state?: KpiStatus
61
-
62
- @Field(type => String, { nullable: true, description: 'Thumbnail image or file path for this KPI.' })
63
- thumbnail?: string
64
-
65
- @Column({ type: 'simple-json', nullable: true })
66
- @Field(type => ScalarObject, {
67
- nullable: true,
68
- description: 'Grade configuration for this KPI version'
69
- })
70
- grades?: KpiScores
71
-
72
- @Column({ type: 'float', nullable: true, default: 1 })
73
- @Field({ nullable: true, description: 'Weight for aggregation in parent category.' })
74
- weight?: number
75
-
76
- @Column({ default: 'DAY' })
77
- @Field(type => KpiPeriodType, { nullable: true })
78
- periodType: KpiPeriodType
79
-
80
- @Column({ nullable: true })
81
- @Field({ nullable: true })
82
- createdAt?: Date
83
-
84
- @Column({ nullable: true })
85
- @Field({ nullable: true })
86
- updatedAt?: Date
87
-
88
- @Column({ nullable: true })
89
- @Field({ nullable: true })
90
- deletedAt?: Date
91
-
92
- @ManyToOne(type => User, { nullable: true })
93
- @Field(type => User, { nullable: true })
94
- creator?: User
95
-
96
- @RelationId((kpi: Kpi) => kpi.creator)
97
- creatorId?: string
98
-
99
- @ManyToOne(type => User, { nullable: true })
100
- @Field(type => User, { nullable: true })
101
- updater?: User
102
-
103
- @RelationId((kpi: Kpi) => kpi.updater)
104
- updaterId?: string
105
-
106
- @HistoryOriginalIdColumn()
107
- public originalId!: string
108
-
109
- @HistoryActionColumn({
110
- nullable: false,
111
- type:
112
- DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
113
- ? 'enum'
114
- : DATABASE_TYPE == 'oracle'
115
- ? 'varchar2'
116
- : DATABASE_TYPE == 'mssql'
117
- ? 'nvarchar'
118
- : 'varchar',
119
- enum:
120
- DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
121
- ? HistoryActionType
122
- : undefined,
123
- length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32
124
- })
125
- public action!: HistoryActionType
126
- }