@things-factory/kpi 9.2.5 → 10.0.0-beta.10

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 (139) 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/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +18 -18
  44. package/client/tsconfig.json +0 -11
  45. package/dist-server/tsconfig.json +0 -10
  46. package/server/@types/index.d.ts +0 -11
  47. package/server/calculator/evaluator.ts +0 -45
  48. package/server/calculator/functions.ts +0 -67
  49. package/server/calculator/index.ts +0 -4
  50. package/server/calculator/parser.ts +0 -137
  51. package/server/calculator/provider.ts +0 -10
  52. package/server/controllers/index.ts +0 -2
  53. package/server/controllers/kpi-metric-value-provider.ts +0 -79
  54. package/server/controllers/kpi-value-provider.ts +0 -51
  55. package/server/index.ts +0 -6
  56. package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
  57. package/server/migrations/1752190849681-seed-kpi.ts +0 -356
  58. package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
  59. package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
  60. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
  61. package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
  62. package/server/migrations/grade-data/x11-performance-table.json +0 -962
  63. package/server/migrations/grade-data/x12-performance-table.json +0 -611
  64. package/server/migrations/grade-data/x14-performance-table.json +0 -42
  65. package/server/migrations/grade-data/x21-performance-table.json +0 -889
  66. package/server/migrations/grade-data/x22-performance-table.json +0 -1064
  67. package/server/migrations/grade-data/x23-performance-table.json +0 -42
  68. package/server/migrations/grade-data/x31-performance-table.json +0 -644
  69. package/server/migrations/grade-data/x32-performance-table.json +0 -993
  70. package/server/migrations/grade-data/x33-performance-table.json +0 -195
  71. package/server/migrations/grade-data/x34-performance-table.json +0 -12
  72. package/server/migrations/grade-data/x35-performance-table.json +0 -42
  73. package/server/migrations/grade-data/x41-performance-table.json +0 -825
  74. package/server/migrations/grade-data/x42-performance-table.json +0 -786
  75. package/server/migrations/grade-data/x43-performance-table.json +0 -12
  76. package/server/migrations/grade-data/x44-performance-table.json +0 -42
  77. package/server/migrations/grade-data/x51-performance-table.json +0 -924
  78. package/server/migrations/grade-data/x52-performance-table.json +0 -42
  79. package/server/migrations/grade-data/x61-performance-table.json +0 -261
  80. package/server/migrations/grade-data/x62-performance-table.json +0 -42
  81. package/server/migrations/index.ts +0 -9
  82. package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
  83. package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
  84. package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
  85. package/server/migrations/seed-data/kpi-values-seed.json +0 -402
  86. package/server/migrations/seed-data/kpis-seed.json +0 -488
  87. package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
  88. package/server/routes.ts +0 -81
  89. package/server/service/index.ts +0 -51
  90. package/server/service/kpi/aggregate-kpi.ts +0 -103
  91. package/server/service/kpi/event-subscriber.ts +0 -29
  92. package/server/service/kpi/index.ts +0 -9
  93. package/server/service/kpi/kpi-formula.service.ts +0 -164
  94. package/server/service/kpi/kpi-grade.types.ts +0 -28
  95. package/server/service/kpi/kpi-history.ts +0 -126
  96. package/server/service/kpi/kpi-mutation.ts +0 -553
  97. package/server/service/kpi/kpi-query.ts +0 -224
  98. package/server/service/kpi/kpi-type.ts +0 -151
  99. package/server/service/kpi/kpi.ts +0 -254
  100. package/server/service/kpi-alert/index.ts +0 -3
  101. package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
  102. package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
  103. package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
  104. package/server/service/kpi-metric/index.ts +0 -7
  105. package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
  106. package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
  107. package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
  108. package/server/service/kpi-metric/kpi-metric.ts +0 -134
  109. package/server/service/kpi-metric-value/index.ts +0 -7
  110. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
  111. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
  112. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
  113. package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
  114. package/server/service/kpi-org-scope/index.ts +0 -6
  115. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
  116. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
  117. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
  118. package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
  119. package/server/service/kpi-scope/index.ts +0 -11
  120. package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
  121. package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
  122. package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
  123. package/server/service/kpi-scope/kpi-scope.ts +0 -143
  124. package/server/service/kpi-statistic/index.ts +0 -7
  125. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
  126. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
  127. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
  128. package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
  129. package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
  130. package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
  131. package/server/service/kpi-value/index.ts +0 -7
  132. package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
  133. package/server/service/kpi-value/kpi-value-query.ts +0 -61
  134. package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
  135. package/server/service/kpi-value/kpi-value-type.ts +0 -122
  136. package/server/service/kpi-value/kpi-value.ts +0 -160
  137. package/server/service/utils/value-date-util.ts +0 -119
  138. package/server/tsconfig.json +0 -10
  139. package/server/types/global.d.ts +0 -8
@@ -1,173 +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
-
5
- import { KpiOrgScope } from './kpi-org-scope'
6
- import { NewKpiOrgScope, KpiOrgScopePatch } from './kpi-org-scope-type'
7
-
8
- @Resolver(KpiOrgScope)
9
- export class KpiOrgScopeMutation {
10
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
11
- @Directive('@transaction')
12
- @Mutation(returns => KpiOrgScope, { description: 'Create a new KPI org-scope mapping with the provided details.' })
13
- async createKpiOrgScope(
14
- @Arg('kpiOrgScope', { description: 'Input object containing details for the new org-scope mapping.' })
15
- kpiOrgScope: NewKpiOrgScope,
16
- @Ctx() context: ResolverContext
17
- ): Promise<KpiOrgScope> {
18
- const { domain, user, tx } = context.state
19
-
20
- // Check if org already exists for this domain
21
- const existingOrgScope = await getRepository(KpiOrgScope, tx).findOne({
22
- where: { domain: { id: domain.id }, org: kpiOrgScope.org }
23
- })
24
-
25
- if (existingOrgScope) {
26
- throw new Error(`Organization '${kpiOrgScope.org}' already has a scope mapping in this domain`)
27
- }
28
-
29
- const entity: Partial<KpiOrgScope> = {
30
- ...kpiOrgScope,
31
- domain,
32
- creator: user,
33
- updater: user
34
- }
35
-
36
- return await getRepository(KpiOrgScope, tx).save(entity)
37
- }
38
-
39
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
40
- @Directive('@transaction')
41
- @Mutation(returns => [KpiOrgScope], {
42
- description: 'Update multiple KPI org-scope mappings with the provided patches.'
43
- })
44
- async updateMultipleKpiOrgScope(
45
- @Arg('patches', type => [KpiOrgScopePatch], {
46
- description: 'Array of patch objects for updating org-scope mappings.'
47
- })
48
- patches: KpiOrgScopePatch[],
49
- @Ctx() context: ResolverContext
50
- ): Promise<KpiOrgScope[]> {
51
- const { domain, user, tx } = context.state
52
-
53
- const ids = patches.map(patch => patch.id)
54
- const existingOrgScopes = await getRepository(KpiOrgScope, tx).find({
55
- where: {
56
- domain: { id: domain.id },
57
- id: In(ids)
58
- }
59
- })
60
-
61
- if (existingOrgScopes.length !== patches.length) {
62
- throw new Error('Some org-scope mappings not found or not accessible')
63
- }
64
-
65
- const updatedOrgScopes: KpiOrgScope[] = []
66
-
67
- for (const patch of patches) {
68
- const existingOrgScope = existingOrgScopes.find(os => os.id === patch.id)
69
- if (!existingOrgScope) continue
70
-
71
- // Check if org is being changed and if new org already exists
72
- if (patch.org && patch.org !== existingOrgScope.org) {
73
- const duplicateOrgScope = await getRepository(KpiOrgScope, tx).findOne({
74
- where: {
75
- domain: { id: domain.id },
76
- org: patch.org,
77
- id: In(ids.filter(id => id !== patch.id))
78
- }
79
- })
80
- if (duplicateOrgScope) {
81
- throw new Error(`Organization '${patch.org}' already has a scope mapping in this domain`)
82
- }
83
- }
84
-
85
- const updatedEntity: Partial<KpiOrgScope> = {
86
- ...existingOrgScope,
87
- ...patch,
88
- updater: user,
89
- updatedAt: new Date()
90
- }
91
-
92
- const savedOrgScope = await getRepository(KpiOrgScope, tx).save(updatedEntity)
93
- updatedOrgScopes.push(savedOrgScope)
94
- }
95
-
96
- return updatedOrgScopes
97
- }
98
-
99
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
100
- @Directive('@transaction')
101
- @Mutation(returns => Boolean, { description: 'Delete KPI org-scope mappings by their IDs.' })
102
- async deleteKpiOrgScopes(
103
- @Arg('ids', type => [String], { description: 'Array of org-scope mapping IDs to delete.' }) ids: string[],
104
- @Ctx() context: ResolverContext
105
- ): Promise<boolean> {
106
- const { domain, tx } = context.state
107
-
108
- const existingOrgScopes = await getRepository(KpiOrgScope, tx).find({
109
- where: {
110
- domain: { id: domain.id },
111
- id: In(ids)
112
- }
113
- })
114
-
115
- if (existingOrgScopes.length === 0) {
116
- throw new Error('No org-scope mappings found to delete')
117
- }
118
-
119
- // Soft delete
120
- await getRepository(KpiOrgScope, tx).softDelete(existingOrgScopes.map(os => os.id))
121
-
122
- return true
123
- }
124
-
125
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
126
- @Directive('@transaction')
127
- @Mutation(returns => [KpiOrgScope], { description: 'Bulk create or update KPI org-scope mappings.' })
128
- async bulkUpsertKpiOrgScopes(
129
- @Arg('orgScopes', type => [NewKpiOrgScope], { description: 'Array of org-scope mapping data for bulk upsert.' })
130
- orgScopes: NewKpiOrgScope[],
131
- @Ctx() context: ResolverContext
132
- ): Promise<KpiOrgScope[]> {
133
- const { domain, user, tx } = context.state
134
-
135
- const orgs = orgScopes.map(os => os.org)
136
- const existingOrgScopes = await getRepository(KpiOrgScope, tx).find({
137
- where: {
138
- domain: { id: domain.id },
139
- org: In(orgs)
140
- }
141
- })
142
-
143
- const results: KpiOrgScope[] = []
144
-
145
- for (const orgScopeData of orgScopes) {
146
- const existing = existingOrgScopes.find(os => os.org === orgScopeData.org)
147
-
148
- if (existing) {
149
- // Update existing
150
- const updatedEntity: Partial<KpiOrgScope> = {
151
- ...existing,
152
- ...orgScopeData,
153
- updater: user,
154
- updatedAt: new Date()
155
- }
156
- const updated = await getRepository(KpiOrgScope, tx).save(updatedEntity)
157
- results.push(updated)
158
- } else {
159
- // Create new
160
- const newEntity: Partial<KpiOrgScope> = {
161
- ...orgScopeData,
162
- domain,
163
- creator: user,
164
- updater: user
165
- }
166
- const saved = await getRepository(KpiOrgScope, tx).save(newEntity)
167
- results.push(saved)
168
- }
169
- }
170
-
171
- return results
172
- }
173
- }
@@ -1,127 +0,0 @@
1
- import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
2
- import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
3
- import { User } from '@things-factory/auth-base'
4
-
5
- import { KpiOrgScope } from './kpi-org-scope'
6
- import { KpiOrgScopeList } from './kpi-org-scope-type'
7
-
8
- @Resolver(KpiOrgScope)
9
- export class KpiOrgScopeQuery {
10
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
11
- @Query(returns => KpiOrgScope!, {
12
- nullable: true,
13
- description: 'Fetch a single KPI org-scope mapping by its unique identifier.'
14
- })
15
- async kpiOrgScope(
16
- @Arg('id', { description: 'Unique identifier of the org-scope mapping to fetch.' }) id: string,
17
- @Ctx() context: ResolverContext
18
- ): Promise<KpiOrgScope> {
19
- const { domain } = context.state
20
-
21
- return await getRepository(KpiOrgScope).findOne({
22
- where: { domain: { id: domain.id }, id }
23
- })
24
- }
25
-
26
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
27
- @Query(returns => KpiOrgScopeList, {
28
- description: 'Fetch multiple KPI org-scope mappings with filters and pagination'
29
- })
30
- async kpiOrgScopes(
31
- @Args(type => ListParam) params: ListParam,
32
- @Ctx() context: ResolverContext
33
- ): Promise<KpiOrgScopeList> {
34
- const { domain } = context.state
35
-
36
- const queryBuilder = getQueryBuilderFromListParams({
37
- domain,
38
- params,
39
- repository: await getRepository(KpiOrgScope),
40
- searchables: ['org', 'scope01', 'scope02', 'scope03', 'scope04', 'scope05']
41
- })
42
-
43
- const [items, total] = await queryBuilder.getManyAndCount()
44
-
45
- return { items, total }
46
- }
47
-
48
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
49
- @Query(returns => [String], { description: 'Get distinct values for a specific scope field' })
50
- async kpiOrgScopeValues(
51
- @Arg('scopeField', { description: 'Scope field to get distinct values from' })
52
- scopeField: 'scope01' | 'scope02' | 'scope03' | 'scope04' | 'scope05',
53
- @Ctx() context: ResolverContext,
54
- @Arg('searchTerm', { nullable: true, description: 'Optional search term to filter values' }) searchTerm?: string
55
- ): Promise<string[]> {
56
- const { domain } = context.state
57
-
58
- const queryBuilder = getRepository(KpiOrgScope)
59
- .createQueryBuilder('orgScope')
60
- .select(`DISTINCT orgScope.${scopeField}`, 'value')
61
- .where('orgScope.domain_id = :domainId', { domainId: domain.id })
62
- .andWhere(`orgScope.${scopeField} IS NOT NULL`)
63
- .andWhere(`orgScope.${scopeField} != ''`)
64
- .andWhere('orgScope.active = true')
65
-
66
- if (searchTerm) {
67
- queryBuilder.andWhere(`orgScope.${scopeField} ILIKE :searchTerm`, {
68
- searchTerm: `%${searchTerm}%`
69
- })
70
- }
71
-
72
- const results = await queryBuilder.getRawMany()
73
- return results.map(r => r.value).filter(v => v)
74
- }
75
-
76
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
77
- @Query(returns => [String], { description: 'Get distinct org values' })
78
- async kpiOrgValues(
79
- @Ctx() context: ResolverContext,
80
- @Arg('searchTerm', { nullable: true, description: 'Optional search term to filter org values' }) searchTerm?: string
81
- ): Promise<string[]> {
82
- const { domain } = context.state
83
-
84
- const queryBuilder = getRepository(KpiOrgScope)
85
- .createQueryBuilder('orgScope')
86
- .select('DISTINCT orgScope.org', 'value')
87
- .where('orgScope.domain_id = :domainId', { domainId: domain.id })
88
- .andWhere('orgScope.active = true')
89
-
90
- if (searchTerm) {
91
- queryBuilder.andWhere('orgScope.org ILIKE :searchTerm', {
92
- searchTerm: `%${searchTerm}%`
93
- })
94
- }
95
-
96
- const results = await queryBuilder.getRawMany()
97
- return results.map(r => r.value)
98
- }
99
-
100
- @Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
101
- @Query(returns => KpiOrgScope, { nullable: true, description: 'Find org-scope mapping by org identifier' })
102
- async kpiOrgScopeByOrg(
103
- @Arg('org', { description: 'Organization identifier to search for' }) org: string,
104
- @Ctx() context: ResolverContext
105
- ): Promise<KpiOrgScope | undefined> {
106
- const { domain } = context.state
107
-
108
- return await getRepository(KpiOrgScope).findOne({
109
- where: { domain: { id: domain.id }, org }
110
- })
111
- }
112
-
113
- @FieldResolver(type => Domain)
114
- async domain(@Root() kpiOrgScope: KpiOrgScope): Promise<Domain> {
115
- return kpiOrgScope.domainId && (await getRepository(Domain).findOneBy({ id: kpiOrgScope.domainId }))
116
- }
117
-
118
- @FieldResolver(type => User)
119
- async creator(@Root() kpiOrgScope: KpiOrgScope): Promise<User> {
120
- return kpiOrgScope.creatorId && (await getRepository(User).findOneBy({ id: kpiOrgScope.creatorId }))
121
- }
122
-
123
- @FieldResolver(type => User)
124
- async updater(@Root() kpiOrgScope: KpiOrgScope): Promise<User> {
125
- return kpiOrgScope.updaterId && (await getRepository(User).findOneBy({ id: kpiOrgScope.updaterId }))
126
- }
127
- }
@@ -1,68 +0,0 @@
1
- import { InputType, Field, ID, ObjectType, Int } from 'type-graphql'
2
- import { KpiOrgScope } from './kpi-org-scope'
3
-
4
- @InputType({ description: 'Input type for creating a new KPI org-scope mapping' })
5
- export class NewKpiOrgScope {
6
- @Field({ description: 'Organizational unit identifier' })
7
- org: string
8
-
9
- @Field({ nullable: true, description: 'First scope dimension' })
10
- scope01?: string
11
-
12
- @Field({ nullable: true, description: 'Second scope dimension' })
13
- scope02?: string
14
-
15
- @Field({ nullable: true, description: 'Third scope dimension' })
16
- scope03?: string
17
-
18
- @Field({ nullable: true, description: 'Fourth scope dimension' })
19
- scope04?: string
20
-
21
- @Field({ nullable: true, description: 'Fifth scope dimension' })
22
- scope05?: string
23
-
24
- @Field({ nullable: true, description: 'Human-readable name or description' })
25
- displayName?: string
26
-
27
- @Field({ nullable: true, description: 'Active status' })
28
- active?: boolean
29
- }
30
-
31
- @InputType({ description: 'Input type for updating an existing KPI org-scope mapping' })
32
- export class KpiOrgScopePatch {
33
- @Field(type => ID, { description: 'ID of the org-scope mapping to update' })
34
- id: string
35
-
36
- @Field({ nullable: true, description: 'Organizational unit identifier' })
37
- org?: string
38
-
39
- @Field({ nullable: true, description: 'First scope dimension' })
40
- scope01?: string
41
-
42
- @Field({ nullable: true, description: 'Second scope dimension' })
43
- scope02?: string
44
-
45
- @Field({ nullable: true, description: 'Third scope dimension' })
46
- scope03?: string
47
-
48
- @Field({ nullable: true, description: 'Fourth scope dimension' })
49
- scope04?: string
50
-
51
- @Field({ nullable: true, description: 'Fifth scope dimension' })
52
- scope05?: string
53
-
54
- @Field({ nullable: true, description: 'Human-readable name or description' })
55
- displayName?: string
56
-
57
- @Field({ nullable: true, description: 'Active status' })
58
- active?: boolean
59
- }
60
-
61
- @ObjectType({ description: 'List of KPI org-scope mappings with pagination' })
62
- export class KpiOrgScopeList {
63
- @Field(type => [KpiOrgScope], { description: 'Array of KPI org-scope mappings' })
64
- items: KpiOrgScope[]
65
-
66
- @Field(type => Int, { description: 'Total count of items' })
67
- total: number
68
- }
@@ -1,123 +0,0 @@
1
- import {
2
- CreateDateColumn,
3
- UpdateDateColumn,
4
- DeleteDateColumn,
5
- Entity,
6
- Index,
7
- Column,
8
- RelationId,
9
- ManyToOne,
10
- PrimaryGeneratedColumn,
11
- EntityManager
12
- } from 'typeorm'
13
- import { ObjectType, Field, ID } from 'type-graphql'
14
-
15
- import { Domain } from '@things-factory/shell'
16
- import { User } from '@things-factory/auth-base'
17
-
18
- @Entity()
19
- @Index('ix_kpi_org_scope_0', (orgScope: KpiOrgScope) => [orgScope.domain, orgScope.entityType, orgScope.entityId], {
20
- unique: true
21
- })
22
- @ObjectType({
23
- description:
24
- 'KPI organization scope mapping entity. Maps business entities to various scope dimensions for KPI analysis using generic reference pattern.'
25
- })
26
- export class KpiOrgScope {
27
- @PrimaryGeneratedColumn('uuid')
28
- @Field(type => ID, { description: 'Unique identifier for this org-scope mapping.' })
29
- readonly id: string
30
-
31
- @ManyToOne(type => Domain)
32
- @Field({ nullable: true, description: 'Domain (tenant) to which this org-scope mapping belongs.' })
33
- domain?: Domain
34
-
35
- @RelationId((orgScope: KpiOrgScope) => orgScope.domain)
36
- @Field({ nullable: true, description: 'ID of the domain (tenant) for this org-scope mapping.' })
37
- domainId?: string
38
-
39
- @Column()
40
- @Field({ description: 'Type of the referenced business entity (e.g., Project, Department, Company, Employee)' })
41
- entityType: string
42
-
43
- @Column()
44
- @Field({ description: 'ID of the referenced business entity' })
45
- entityId: string
46
-
47
- @Column()
48
- @Field({ description: 'Name of the referenced business entity (denormalized for performance and convenience)' })
49
- entityName: string
50
-
51
- @Column({ nullable: true })
52
- @Field({ nullable: true, description: 'Legacy org field for backward compatibility' })
53
- org?: string
54
-
55
- @Column({ nullable: true })
56
- @Field({ nullable: true, description: 'First scope dimension (e.g., geographical region, business unit)' })
57
- scope01?: string
58
-
59
- @Column({ nullable: true })
60
- @Field({ nullable: true, description: 'Second scope dimension (e.g., product line, customer segment)' })
61
- scope02?: string
62
-
63
- @Column({ nullable: true })
64
- @Field({ nullable: true, description: 'Third scope dimension (e.g., process type, technology stack)' })
65
- scope03?: string
66
-
67
- @Column({ nullable: true })
68
- @Field({ nullable: true, description: 'Fourth scope dimension (e.g., cost center, profit center)' })
69
- scope04?: string
70
-
71
- @Column({ nullable: true })
72
- @Field({ nullable: true, description: 'Fifth scope dimension (e.g., risk level, priority tier)' })
73
- scope05?: string
74
-
75
- @CreateDateColumn()
76
- @Field({ nullable: true, description: 'Timestamp when this org-scope mapping was created.' })
77
- createdAt?: Date
78
-
79
- @UpdateDateColumn()
80
- @Field({ nullable: true, description: 'Timestamp when this org-scope mapping was last updated.' })
81
- updatedAt?: Date
82
-
83
- @ManyToOne(type => User, { nullable: true })
84
- @Field(type => User, { nullable: true, description: 'User who created this org-scope mapping.' })
85
- creator?: User
86
-
87
- @RelationId((orgScope: KpiOrgScope) => orgScope.creator)
88
- @Field({ nullable: true, description: 'ID of the user who created this org-scope mapping.' })
89
- creatorId?: string
90
-
91
- @ManyToOne(type => User, { nullable: true })
92
- @Field(type => User, { nullable: true, description: 'User who last updated this org-scope mapping.' })
93
- updater?: User
94
-
95
- @RelationId((orgScope: KpiOrgScope) => orgScope.updater)
96
- @Field({ nullable: true, description: 'ID of the user who last updated this org-scope mapping.' })
97
- updaterId?: string
98
-
99
- // Helper methods for Generic Reference Pattern
100
- async getReferencedEntity(entityManager: EntityManager): Promise<any> {
101
- try {
102
- const repository = entityManager.getRepository(this.entityType)
103
- return await repository.findOne({ where: { id: this.entityId } })
104
- } catch (error) {
105
- console.warn(`Failed to get referenced entity ${this.entityType}:${this.entityId}`, error)
106
- return null
107
- }
108
- }
109
-
110
- async validateEntityReference(entityManager: EntityManager): Promise<boolean> {
111
- try {
112
- const entity = await this.getReferencedEntity(entityManager)
113
- return !!entity
114
- } catch {
115
- return false
116
- }
117
- }
118
-
119
- // Convenience getter for display purposes
120
- get displayName(): string {
121
- return this.entityName || `${this.entityType}:${this.entityId}` || this.org || 'Unknown'
122
- }
123
- }
@@ -1,11 +0,0 @@
1
- import { KpiScope } from './kpi-scope'
2
- import { KpiScopeQuery } from './kpi-scope-query'
3
- import { KpiScopeMutation } from './kpi-scope-mutation'
4
-
5
- export const entities = [KpiScope]
6
- export const resolvers = [KpiScopeQuery, KpiScopeMutation]
7
-
8
- export * from './kpi-scope'
9
- export * from './kpi-scope-query'
10
- export * from './kpi-scope-mutation'
11
- export * from './kpi-scope-type'
@@ -1,129 +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 type ResolverContext from '@things-factory/auth-base'
5
-
6
- import { KpiScope } from './kpi-scope'
7
- import { NewKpiScope, KpiScopePatch } from './kpi-scope-type'
8
-
9
- @Resolver(KpiScope)
10
- export class KpiScopeMutation {
11
- @Directive('@transaction')
12
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
13
- @Mutation(returns => KpiScope, { description: 'Create a new scope dimension definition' })
14
- async createKpiScope(
15
- @Arg('kpiScope') kpiScope: NewKpiScope,
16
- @Ctx() context: ResolverContext
17
- ): Promise<KpiScope> {
18
- const { domain, user, tx } = context.state
19
-
20
- const result = await getRepository(KpiScope, tx).save({
21
- ...kpiScope,
22
- domain,
23
- creator: user,
24
- updater: user
25
- })
26
-
27
- return result
28
- }
29
-
30
- @Directive('@transaction')
31
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
32
- @Mutation(returns => KpiScope, { description: 'Update a scope dimension definition' })
33
- async updateKpiScope(
34
- @Arg('id') id: string,
35
- @Arg('patch') patch: KpiScopePatch,
36
- @Ctx() context: ResolverContext
37
- ): Promise<KpiScope> {
38
- const { domain, user, tx } = context.state
39
-
40
- const repository = getRepository(KpiScope, tx)
41
- const kpiScope = await repository.findOne({
42
- where: { domain: { id: domain.id }, id }
43
- })
44
-
45
- const result = await repository.save({
46
- ...kpiScope,
47
- ...patch,
48
- updater: user
49
- })
50
-
51
- return result
52
- }
53
-
54
- @Directive('@transaction')
55
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
56
- @Mutation(returns => [KpiScope], { description: "Update multiple scope dimension definitions" })
57
- async updateMultipleKpiScope(
58
- @Arg('patches', type => [KpiScopePatch]) patches: KpiScopePatch[],
59
- @Ctx() context: ResolverContext
60
- ): Promise<KpiScope[]> {
61
- const { domain, user, tx } = context.state
62
-
63
- let results = []
64
- const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
65
- const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
66
- const kpiScopeRepo = getRepository(KpiScope, tx)
67
-
68
- if (_createRecords.length > 0) {
69
- for (let i = 0; i < _createRecords.length; i++) {
70
- const newRecord = _createRecords[i]
71
-
72
- const result = await kpiScopeRepo.save({
73
- ...newRecord,
74
- domain,
75
- creator: user,
76
- updater: user
77
- })
78
-
79
- results.push({ ...result, cuFlag: '+' })
80
- }
81
- }
82
-
83
- if (_updateRecords.length > 0) {
84
- for (let i = 0; i < _updateRecords.length; i++) {
85
- const updateRecord = _updateRecords[i]
86
- const kpiScope = await kpiScopeRepo.findOneBy({ id: updateRecord.id })
87
-
88
- const result = await kpiScopeRepo.save({
89
- ...kpiScope,
90
- ...updateRecord,
91
- updater: user
92
- })
93
-
94
- results.push({ ...result, cuFlag: 'M' })
95
- }
96
- }
97
-
98
- return results
99
- }
100
-
101
- @Directive('@transaction')
102
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
103
- @Mutation(returns => Boolean, { description: 'Delete a scope dimension definition' })
104
- async deleteKpiScope(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
105
- const { domain, tx } = context.state
106
-
107
- await getRepository(KpiScope, tx).delete({ domain: { id: domain.id }, id })
108
-
109
- return true
110
- }
111
-
112
- @Directive('@transaction')
113
- @Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
114
- @Mutation(returns => Boolean, { description: 'Delete multiple scope dimension definitions' })
115
- async deleteKpiScopes(
116
- @Arg('ids', type => [String]) ids: string[],
117
- @Ctx() context: ResolverContext
118
- ): Promise<boolean> {
119
- const { domain, tx } = context.state
120
-
121
- await getRepository(KpiScope, tx).delete({
122
- domain: { id: domain.id },
123
- id: In(ids)
124
- })
125
-
126
- return true
127
- }
128
-
129
- }