@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.
- package/client/pages/kpi/kpi-list-page.ts +339 -525
- package/client/pages/kpi/kpi-tree-page.ts +135 -207
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
- package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
- package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
- package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
- package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
- package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
- package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
- package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
- package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
- package/dist-client/pages/kpi/kpi-list-page.js +150 -282
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
- package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/client/tsconfig.json +0 -11
- package/dist-server/tsconfig.json +0 -10
- package/server/@types/index.d.ts +0 -11
- package/server/calculator/evaluator.ts +0 -45
- package/server/calculator/functions.ts +0 -67
- package/server/calculator/index.ts +0 -4
- package/server/calculator/parser.ts +0 -137
- package/server/calculator/provider.ts +0 -10
- package/server/controllers/index.ts +0 -2
- package/server/controllers/kpi-metric-value-provider.ts +0 -79
- package/server/controllers/kpi-value-provider.ts +0 -51
- package/server/index.ts +0 -6
- package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
- package/server/migrations/1752190849681-seed-kpi.ts +0 -356
- package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
- package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
- package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
- package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
- package/server/migrations/grade-data/x11-performance-table.json +0 -962
- package/server/migrations/grade-data/x12-performance-table.json +0 -611
- package/server/migrations/grade-data/x14-performance-table.json +0 -42
- package/server/migrations/grade-data/x21-performance-table.json +0 -889
- package/server/migrations/grade-data/x22-performance-table.json +0 -1064
- package/server/migrations/grade-data/x23-performance-table.json +0 -42
- package/server/migrations/grade-data/x31-performance-table.json +0 -644
- package/server/migrations/grade-data/x32-performance-table.json +0 -993
- package/server/migrations/grade-data/x33-performance-table.json +0 -195
- package/server/migrations/grade-data/x34-performance-table.json +0 -12
- package/server/migrations/grade-data/x35-performance-table.json +0 -42
- package/server/migrations/grade-data/x41-performance-table.json +0 -825
- package/server/migrations/grade-data/x42-performance-table.json +0 -786
- package/server/migrations/grade-data/x43-performance-table.json +0 -12
- package/server/migrations/grade-data/x44-performance-table.json +0 -42
- package/server/migrations/grade-data/x51-performance-table.json +0 -924
- package/server/migrations/grade-data/x52-performance-table.json +0 -42
- package/server/migrations/grade-data/x61-performance-table.json +0 -261
- package/server/migrations/grade-data/x62-performance-table.json +0 -42
- package/server/migrations/index.ts +0 -9
- package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
- package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
- package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
- package/server/migrations/seed-data/kpi-values-seed.json +0 -402
- package/server/migrations/seed-data/kpis-seed.json +0 -488
- package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
- package/server/routes.ts +0 -81
- package/server/service/index.ts +0 -51
- package/server/service/kpi/aggregate-kpi.ts +0 -103
- package/server/service/kpi/event-subscriber.ts +0 -29
- package/server/service/kpi/index.ts +0 -9
- package/server/service/kpi/kpi-formula.service.ts +0 -164
- package/server/service/kpi/kpi-grade.types.ts +0 -28
- package/server/service/kpi/kpi-history.ts +0 -126
- package/server/service/kpi/kpi-mutation.ts +0 -553
- package/server/service/kpi/kpi-query.ts +0 -224
- package/server/service/kpi/kpi-type.ts +0 -151
- package/server/service/kpi/kpi.ts +0 -254
- package/server/service/kpi-alert/index.ts +0 -3
- package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
- package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
- package/server/service/kpi-metric/index.ts +0 -7
- package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
- package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
- package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
- package/server/service/kpi-metric/kpi-metric.ts +0 -134
- package/server/service/kpi-metric-value/index.ts +0 -7
- package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
- package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
- package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
- package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
- package/server/service/kpi-org-scope/index.ts +0 -6
- package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
- package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
- package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
- package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
- package/server/service/kpi-scope/index.ts +0 -11
- package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
- package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
- package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
- package/server/service/kpi-scope/kpi-scope.ts +0 -143
- package/server/service/kpi-statistic/index.ts +0 -7
- package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
- package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
- package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
- package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
- package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
- package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
- package/server/service/kpi-value/index.ts +0 -7
- package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
- package/server/service/kpi-value/kpi-value-query.ts +0 -61
- package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
- package/server/service/kpi-value/kpi-value-type.ts +0 -122
- package/server/service/kpi-value/kpi-value.ts +0 -160
- package/server/service/utils/value-date-util.ts +0 -119
- package/server/tsconfig.json +0 -10
- 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]
|