@things-factory/kpi 9.0.31 → 9.0.33
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/README.md +1 -2
- package/client/charts/kpi-boxplot-chart.ts +182 -42
- package/client/charts/kpi-radar-chart.ts +9 -9
- package/client/pages/kpi/kpi-list-page.ts +196 -32
- package/client/pages/kpi/kpi-overview.ts +9 -11
- package/client/pages/kpi/kpi-tree-page.ts +409 -0
- package/client/pages/kpi/kpi-view.ts +187 -0
- package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +1 -1
- package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +1 -1
- package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +1 -1
- package/client/pages/kpi-dashboard/components/kpi-left-panel.ts +198 -160
- package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +133 -0
- package/client/pages/kpi-dashboard/components/kpi-region-popup.ts +3 -2
- package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +291 -48
- package/client/pages/kpi-dashboard/kpi-dashboard.ts +28 -30
- package/client/pages/kpi-history/kpi-history-list-page.ts +11 -11
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +10 -2
- package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +7 -7
- package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +2 -2
- package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +16 -8
- package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.ts +5 -5
- package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +1 -2
- package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +10 -2
- package/client/pages/kpi-value/kpi-value-editor-page.ts +11 -7
- package/client/pages/kpi-value/kpi-value-list-page.ts +31 -7
- package/client/route.ts +2 -9
- package/design-entities.md +8 -12
- package/dist-client/charts/kpi-boxplot-chart.d.ts +2 -0
- package/dist-client/charts/kpi-boxplot-chart.js +168 -42
- package/dist-client/charts/kpi-boxplot-chart.js.map +1 -1
- package/dist-client/charts/kpi-radar-chart.js +9 -9
- package/dist-client/charts/kpi-radar-chart.js.map +1 -1
- package/dist-client/pages/kpi/kpi-list-page.d.ts +19 -3
- package/dist-client/pages/kpi/kpi-list-page.js +188 -32
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-overview.js +9 -11
- package/dist-client/pages/kpi/kpi-overview.js.map +1 -1
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +59 -0
- package/dist-client/pages/kpi/kpi-tree-page.js +403 -0
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -0
- package/dist-client/pages/kpi/kpi-view.d.ts +12 -0
- package/dist-client/pages/kpi/kpi-view.js +191 -0
- package/dist-client/pages/kpi/kpi-view.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +3 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +197 -161
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +5 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +146 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +3 -2
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +3 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +268 -46
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +28 -30
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +6 -1
- package/dist-client/pages/kpi-history/kpi-history-list-page.js +11 -11
- package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +10 -2
- 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 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +8 -8
- 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-importer.js +2 -2
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +16 -8
- 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-form.d.ts +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +6 -6
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +1 -2
- 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 +5 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +10 -2
- 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 +2 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +16 -8
- 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 +5 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +31 -7
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +2 -8
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/kpi-metric-value-provider.d.ts +1 -1
- package/dist-server/controllers/kpi-metric-value-provider.js +4 -4
- package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -1
- package/dist-server/controllers/kpi-value-provider.d.ts +1 -1
- package/dist-server/controllers/kpi-value-provider.js +3 -3
- package/dist-server/controllers/kpi-value-provider.js.map +1 -1
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +101 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
- package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
- package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
- package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
- package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
- package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
- package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
- package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
- package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
- package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
- package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
- package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
- package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
- package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
- package/dist-server/migrations/seed-data/kpi-metrics-seed.json +454 -0
- package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
- package/dist-server/migrations/seed-data/kpi-scopes-seed.json +121 -0
- package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
- package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
- package/dist-server/migrations/seed-data/scope-definitions-seed.json +90 -0
- package/dist-server/service/index.d.ts +4 -7
- package/dist-server/service/index.js +10 -13
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/kpi/aggregate-kpi.js +30 -13
- package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
- package/dist-server/service/kpi/kpi-formula.service.d.ts +15 -0
- package/dist-server/service/kpi/kpi-formula.service.js +90 -0
- package/dist-server/service/kpi/kpi-formula.service.js.map +1 -1
- package/dist-server/service/kpi/kpi-history.d.ts +0 -3
- package/dist-server/service/kpi/kpi-history.js +0 -10
- package/dist-server/service/kpi/kpi-history.js.map +1 -1
- package/dist-server/service/kpi/kpi-mutation.d.ts +1 -1
- package/dist-server/service/kpi/kpi-mutation.js +57 -20
- package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
- package/dist-server/service/kpi/kpi-query.d.ts +7 -3
- package/dist-server/service/kpi/kpi-query.js +126 -10
- package/dist-server/service/kpi/kpi-query.js.map +1 -1
- package/dist-server/service/kpi/kpi-type.d.ts +4 -2
- package/dist-server/service/kpi/kpi-type.js +12 -4
- package/dist-server/service/kpi/kpi-type.js.map +1 -1
- package/dist-server/service/kpi/kpi.d.ts +4 -3
- package/dist-server/service/kpi/kpi.js +20 -8
- package/dist-server/service/kpi/kpi.js.map +1 -1
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +46 -11
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +6 -6
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +2 -2
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +4 -4
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -3
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
- package/dist-server/service/kpi-org-scope/index.d.ts +5 -0
- package/dist-server/service/kpi-org-scope/index.js +9 -0
- package/dist-server/service/kpi-org-scope/index.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
- package/dist-server/service/kpi-scope/index.d.ts +9 -0
- package/dist-server/service/kpi-scope/index.js +14 -0
- package/dist-server/service/kpi-scope/index.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.d.ts +9 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.js +135 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.d.ts +11 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.js +89 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.d.ts +35 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.js +138 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope.d.ts +38 -0
- package/dist-server/service/kpi-scope/kpi-scope.js +144 -0
- package/dist-server/service/kpi-scope/kpi-scope.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.d.ts +43 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js +181 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.d.ts +50 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js +324 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.d.ts +4 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js +76 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js.map +1 -1
- package/dist-server/service/kpi-statistic/kpi-statistic-query.d.ts +5 -1
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js +92 -1
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js.map +1 -1
- package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +4 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js +33 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-mutation.js +71 -7
- package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-type.d.ts +4 -2
- package/dist-server/service/kpi-value/kpi-value-type.js +12 -4
- package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value.d.ts +3 -1
- package/dist-server/service/kpi-value/kpi-value.js +11 -5
- package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
- package/dist-server/service/utils/value-date-util.d.ts +1 -0
- package/dist-server/service/utils/value-date-util.js +41 -0
- package/dist-server/service/utils/value-date-util.js.map +1 -1
- package/dist-server/tsconfig.json +10 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/server/@types/index.d.ts +11 -0
- package/server/controllers/kpi-metric-value-provider.ts +5 -5
- package/server/controllers/kpi-value-provider.ts +4 -4
- package/server/migrations/1752190849680-seed-kpi-metrics.ts +124 -0
- package/server/migrations/1752190849681-seed-kpi.ts +356 -0
- package/server/migrations/1752192090123-add-grades-to-kpi.ts +67 -0
- package/server/migrations/1752192090124-add-kpi-statistics.ts +719 -0
- package/server/migrations/1752192090128-seed-kpi-org-scope.ts +132 -0
- package/server/migrations/1752192090129-seed-kpi-values.ts +207 -0
- package/server/migrations/grade-data/x11-performance-table.json +962 -0
- package/server/migrations/grade-data/x12-performance-table.json +611 -0
- package/server/migrations/grade-data/x14-performance-table.json +42 -0
- package/server/migrations/grade-data/x21-performance-table.json +889 -0
- package/server/migrations/grade-data/x22-performance-table.json +1064 -0
- package/server/migrations/grade-data/x23-performance-table.json +42 -0
- package/server/migrations/grade-data/x31-performance-table.json +644 -0
- package/server/migrations/grade-data/x32-performance-table.json +993 -0
- package/server/migrations/grade-data/x33-performance-table.json +195 -0
- package/server/migrations/grade-data/x34-performance-table.json +12 -0
- package/server/migrations/grade-data/x35-performance-table.json +42 -0
- package/server/migrations/grade-data/x41-performance-table.json +825 -0
- package/server/migrations/grade-data/x42-performance-table.json +786 -0
- package/server/migrations/grade-data/x43-performance-table.json +12 -0
- package/server/migrations/grade-data/x44-performance-table.json +42 -0
- package/server/migrations/grade-data/x51-performance-table.json +924 -0
- package/server/migrations/grade-data/x52-performance-table.json +42 -0
- package/server/migrations/grade-data/x61-performance-table.json +261 -0
- package/server/migrations/grade-data/x62-performance-table.json +42 -0
- package/server/migrations/seed-data/kpi-metrics-seed.json +454 -0
- package/server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
- package/server/migrations/seed-data/kpi-scopes-seed.json +121 -0
- package/server/migrations/seed-data/kpi-values-seed.json +402 -0
- package/server/migrations/seed-data/kpis-seed.json +488 -0
- package/server/migrations/seed-data/scope-definitions-seed.json +90 -0
- package/server/service/index.ts +10 -13
- package/server/service/kpi/aggregate-kpi.ts +31 -13
- package/server/service/kpi/kpi-formula.service.ts +101 -0
- package/server/service/kpi/kpi-history.ts +0 -8
- package/server/service/kpi/kpi-mutation.ts +59 -19
- package/server/service/kpi/kpi-query.ts +119 -8
- package/server/service/kpi/kpi-type.ts +10 -4
- package/server/service/kpi/kpi.ts +17 -7
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +55 -11
- package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +6 -6
- package/server/service/kpi-metric-value/kpi-metric-value-type.ts +4 -4
- package/server/service/kpi-metric-value/kpi-metric-value.ts +3 -3
- package/server/service/kpi-org-scope/index.ts +6 -0
- package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +173 -0
- package/server/service/kpi-org-scope/kpi-org-scope-query.ts +127 -0
- package/server/service/kpi-org-scope/kpi-org-scope-type.ts +68 -0
- package/server/service/kpi-org-scope/kpi-org-scope.ts +123 -0
- package/server/service/kpi-scope/index.ts +11 -0
- package/server/service/kpi-scope/kpi-scope-mutation.ts +129 -0
- package/server/service/kpi-scope/kpi-scope-query.ts +63 -0
- package/server/service/kpi-scope/kpi-scope-type.ts +96 -0
- package/server/service/kpi-scope/kpi-scope.ts +143 -0
- package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +231 -0
- package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +410 -0
- package/server/service/kpi-statistic/kpi-statistic-mutation.ts +97 -0
- package/server/service/kpi-statistic/kpi-statistic-query.ts +89 -2
- package/server/service/kpi-statistic/kpi-statistic.ts +32 -0
- package/server/service/kpi-value/kpi-value-mutation.ts +73 -7
- package/server/service/kpi-value/kpi-value-type.ts +10 -4
- package/server/service/kpi-value/kpi-value.ts +10 -5
- package/server/service/utils/value-date-util.ts +47 -0
- package/server/types/global.d.ts +8 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +15 -3
- package/translations/ja.json +13 -3
- package/translations/ko.json +15 -3
- package/translations/ms.json +13 -3
- package/translations/zh.json +13 -3
- package/client/pages/kpi-category/kpi-category-importer.ts +0 -90
- package/client/pages/kpi-category/kpi-category-list-page.ts +0 -537
- package/client/pages/kpi-category/kpi-category-value-calculator.ts +0 -233
- package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +0 -404
- package/dist-client/pages/kpi-category/kpi-category-importer.d.ts +0 -23
- package/dist-client/pages/kpi-category/kpi-category-importer.js +0 -92
- package/dist-client/pages/kpi-category/kpi-category-importer.js.map +0 -1
- package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +0 -74
- package/dist-client/pages/kpi-category/kpi-category-list-page.js +0 -517
- package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +0 -1
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +0 -13
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +0 -256
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +0 -1
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +0 -63
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +0 -393
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +0 -1
- package/dist-server/service/kpi-category/index.d.ts +0 -6
- package/dist-server/service/kpi-category/index.js +0 -10
- package/dist-server/service/kpi-category/index.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +0 -9
- package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -221
- package/dist-server/service/kpi-category/kpi-category-mutation.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-query.d.ts +0 -18
- package/dist-server/service/kpi-category/kpi-category-query.js +0 -115
- package/dist-server/service/kpi-category/kpi-category-query.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-type.d.ts +0 -24
- package/dist-server/service/kpi-category/kpi-category-type.js +0 -100
- package/dist-server/service/kpi-category/kpi-category-type.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category.d.ts +0 -22
- package/dist-server/service/kpi-category/kpi-category.js +0 -106
- package/dist-server/service/kpi-category/kpi-category.js.map +0 -1
- package/dist-server/service/kpi-category-value/index.d.ts +0 -6
- package/dist-server/service/kpi-category-value/index.js +0 -10
- package/dist-server/service/kpi-category-value/index.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +0 -8
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +0 -102
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +0 -13
- package/dist-server/service/kpi-category-value/kpi-category-value-query.js +0 -91
- package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +0 -19
- package/dist-server/service/kpi-category-value/kpi-category-value-type.js +0 -73
- package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +0 -19
- package/dist-server/service/kpi-category-value/kpi-category-value.js +0 -91
- package/dist-server/service/kpi-category-value/kpi-category-value.js.map +0 -1
- package/helps/kpi/kpi-category.md +0 -160
- package/server/service/kpi-category/index.ts +0 -7
- package/server/service/kpi-category/kpi-category-mutation.ts +0 -217
- package/server/service/kpi-category/kpi-category-query.ts +0 -87
- package/server/service/kpi-category/kpi-category-type.ts +0 -73
- package/server/service/kpi-category/kpi-category.ts +0 -95
- package/server/service/kpi-category-value/index.ts +0 -7
- package/server/service/kpi-category-value/kpi-category-value-mutation.ts +0 -88
- package/server/service/kpi-category-value/kpi-category-value-query.ts +0 -62
- package/server/service/kpi-category-value/kpi-category-value-type.ts +0 -48
- package/server/service/kpi-category-value/kpi-category-value.ts +0 -79
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
|
|
2
2
|
import { In } from 'typeorm'
|
|
3
3
|
import { getRepository } from '@things-factory/shell'
|
|
4
|
+
import type ResolverContext from '@things-factory/auth-base'
|
|
4
5
|
|
|
5
6
|
import { KpiStatistic } from './kpi-statistic.js'
|
|
6
7
|
import { NewKpiStatistic, KpiStatisticPatch } from './kpi-statistic-type.js'
|
|
8
|
+
import { KpiStatisticCalculationService } from './kpi-statistic-calculation.service.js'
|
|
9
|
+
import { KpiPeriodType } from '../kpi/kpi.js'
|
|
7
10
|
|
|
8
11
|
@Resolver(KpiStatistic)
|
|
9
12
|
export class KpiStatisticMutation {
|
|
@@ -191,4 +194,98 @@ export class KpiStatisticMutation {
|
|
|
191
194
|
|
|
192
195
|
return true
|
|
193
196
|
}
|
|
197
|
+
|
|
198
|
+
@Directive('@transaction')
|
|
199
|
+
@Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
|
|
200
|
+
@Mutation(returns => [KpiStatistic], {
|
|
201
|
+
description: 'Calculate statistics for a specific KPI and period from KpiValue data'
|
|
202
|
+
})
|
|
203
|
+
async calculateKpiStatistics(
|
|
204
|
+
@Arg('kpiId') kpiId: string,
|
|
205
|
+
@Arg('periodType', type => KpiPeriodType) periodType: KpiPeriodType,
|
|
206
|
+
@Arg('valueDate') valueDate: string,
|
|
207
|
+
@Ctx() context: ResolverContext
|
|
208
|
+
): Promise<KpiStatistic[]> {
|
|
209
|
+
const overallStats = await KpiStatisticCalculationService.calculateKpiStatistics(
|
|
210
|
+
kpiId,
|
|
211
|
+
periodType,
|
|
212
|
+
valueDate,
|
|
213
|
+
null, // 전체 통계
|
|
214
|
+
context
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
const scopedStats = await KpiStatisticCalculationService.calculateScopedStatistics(
|
|
218
|
+
kpiId,
|
|
219
|
+
periodType,
|
|
220
|
+
valueDate,
|
|
221
|
+
context
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return [overallStats, ...scopedStats].filter(Boolean)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@Directive('@transaction')
|
|
228
|
+
@Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
|
|
229
|
+
@Mutation(returns => [KpiStatistic], {
|
|
230
|
+
description: 'Calculate statistics for all KPIs in a specific period from KpiValue data'
|
|
231
|
+
})
|
|
232
|
+
async calculateAllKpiStatistics(
|
|
233
|
+
@Arg('periodType', type => KpiPeriodType) periodType: KpiPeriodType,
|
|
234
|
+
@Arg('valueDate') valueDate: string,
|
|
235
|
+
@Ctx() context: ResolverContext
|
|
236
|
+
): Promise<KpiStatistic[]> {
|
|
237
|
+
return await KpiStatisticCalculationService.calculateAllStatistics(
|
|
238
|
+
periodType,
|
|
239
|
+
valueDate,
|
|
240
|
+
context
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@Directive('@transaction')
|
|
245
|
+
@Directive('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
|
|
246
|
+
@Mutation(returns => [KpiStatistic], {
|
|
247
|
+
description: 'Recalculate statistics for dashboard regions (scope02-based statistics)'
|
|
248
|
+
})
|
|
249
|
+
async calculateRegionalStatistics(
|
|
250
|
+
@Arg('periodType', type => KpiPeriodType, { defaultValue: KpiPeriodType.MONTH }) periodType: KpiPeriodType,
|
|
251
|
+
@Arg('valueDate') valueDate: string,
|
|
252
|
+
@Ctx() context: ResolverContext
|
|
253
|
+
): Promise<KpiStatistic[]> {
|
|
254
|
+
const { domain, user, tx } = context.state
|
|
255
|
+
|
|
256
|
+
// 지역별(scope02) 통계만 계산
|
|
257
|
+
const kpiStatistics = await getRepository(KpiStatistic, tx)
|
|
258
|
+
.createQueryBuilder('stat')
|
|
259
|
+
.leftJoinAndSelect('stat.kpiOrgScope', 'orgScope')
|
|
260
|
+
.leftJoinAndSelect('stat.kpi', 'kpi')
|
|
261
|
+
.where('stat.domain = :domainId', { domainId: domain.id })
|
|
262
|
+
.andWhere('stat.periodType = :periodType', { periodType })
|
|
263
|
+
.andWhere('stat.valueDate = :valueDate', { valueDate })
|
|
264
|
+
.andWhere('orgScope.scope02 IS NOT NULL') // 지역 정보가 있는 것만
|
|
265
|
+
.getMany()
|
|
266
|
+
|
|
267
|
+
// 계산 결과가 없으면 새로 계산
|
|
268
|
+
if (kpiStatistics.length === 0) {
|
|
269
|
+
return await KpiStatisticCalculationService.calculateAllStatistics(
|
|
270
|
+
periodType,
|
|
271
|
+
valueDate,
|
|
272
|
+
context
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 기존 통계 재계산
|
|
277
|
+
const results: KpiStatistic[] = []
|
|
278
|
+
for (const stat of kpiStatistics) {
|
|
279
|
+
const recalculated = await KpiStatisticCalculationService.calculateKpiStatistics(
|
|
280
|
+
stat.kpiId,
|
|
281
|
+
periodType,
|
|
282
|
+
valueDate,
|
|
283
|
+
stat.kpiOrgScope,
|
|
284
|
+
context
|
|
285
|
+
)
|
|
286
|
+
if (recalculated) results.push(recalculated)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return results
|
|
290
|
+
}
|
|
194
291
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
|
|
2
2
|
import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
|
|
3
3
|
import { User } from '@things-factory/auth-base'
|
|
4
|
+
import type ResolverContext from '@things-factory/auth-base'
|
|
4
5
|
import { KpiStatistic } from './kpi-statistic.js'
|
|
5
6
|
import { KpiStatisticList } from './kpi-statistic-type.js'
|
|
6
|
-
import { Kpi } from '../kpi/kpi.js'
|
|
7
|
+
import { Kpi, KpiPeriodType } from '../kpi/kpi.js'
|
|
8
|
+
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope.js'
|
|
7
9
|
|
|
8
10
|
@Resolver(KpiStatistic)
|
|
9
11
|
export class KpiStatisticQuery {
|
|
@@ -29,7 +31,17 @@ export class KpiStatisticQuery {
|
|
|
29
31
|
domain,
|
|
30
32
|
params,
|
|
31
33
|
repository: await getRepository(KpiStatistic),
|
|
32
|
-
searchables: ['kpi', '
|
|
34
|
+
searchables: ['kpi', 'valueDate'],
|
|
35
|
+
filtersMap: {
|
|
36
|
+
kpi: { columnName: 'id', relationColumn: 'kpi' },
|
|
37
|
+
kpiOrgScope: { columnName: 'id', relationColumn: 'kpiOrgScope' },
|
|
38
|
+
periodType: { columnName: 'periodType' },
|
|
39
|
+
valueDate: { columnName: 'valueDate' },
|
|
40
|
+
scope02: {
|
|
41
|
+
columnName: 'scope02',
|
|
42
|
+
relationColumn: 'kpiOrgScope'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
33
45
|
})
|
|
34
46
|
|
|
35
47
|
const [items, total] = await queryBuilder.getManyAndCount()
|
|
@@ -56,4 +68,79 @@ export class KpiStatisticQuery {
|
|
|
56
68
|
async creator(@Root() kpiStatistic: KpiStatistic): Promise<User> {
|
|
57
69
|
return kpiStatistic.creatorId && (await getRepository(User).findOneBy({ id: kpiStatistic.creatorId }))
|
|
58
70
|
}
|
|
71
|
+
|
|
72
|
+
@FieldResolver(type => KpiOrgScope)
|
|
73
|
+
async kpiOrgScope(@Root() kpiStatistic: KpiStatistic): Promise<KpiOrgScope> {
|
|
74
|
+
return kpiStatistic.kpiOrgScopeId && (await getRepository(KpiOrgScope).findOneBy({ id: kpiStatistic.kpiOrgScopeId }))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
78
|
+
@Query(returns => [KpiStatistic], {
|
|
79
|
+
description: 'Get regional statistics for dashboard map visualization'
|
|
80
|
+
})
|
|
81
|
+
async regionalKpiStatistics(
|
|
82
|
+
@Arg('valueDate') valueDate: string,
|
|
83
|
+
@Arg('periodType', type => KpiPeriodType, { defaultValue: KpiPeriodType.MONTH }) periodType: KpiPeriodType,
|
|
84
|
+
@Arg('regions', type => [String], { nullable: true }) regions: string[] | undefined,
|
|
85
|
+
@Ctx() context: ResolverContext
|
|
86
|
+
): Promise<KpiStatistic[]> {
|
|
87
|
+
const { domain } = context.state
|
|
88
|
+
|
|
89
|
+
const queryBuilder = getRepository(KpiStatistic)
|
|
90
|
+
.createQueryBuilder('stat')
|
|
91
|
+
.leftJoinAndSelect('stat.kpi', 'kpi')
|
|
92
|
+
.leftJoinAndSelect('stat.kpiOrgScope', 'orgScope')
|
|
93
|
+
.where('stat.domain = :domainId', { domainId: domain.id })
|
|
94
|
+
.andWhere('stat.periodType = :periodType', { periodType })
|
|
95
|
+
.andWhere('stat.valueDate = :valueDate', { valueDate })
|
|
96
|
+
.andWhere('orgScope.scope02 IS NOT NULL') // 지역 정보가 있는 것만
|
|
97
|
+
|
|
98
|
+
if (regions && regions.length > 0) {
|
|
99
|
+
queryBuilder.andWhere('orgScope.scope02 IN (:...regions)', { regions })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
queryBuilder.orderBy('orgScope.scope02', 'ASC')
|
|
103
|
+
.addOrderBy('kpi.name', 'ASC')
|
|
104
|
+
|
|
105
|
+
return await queryBuilder.getMany()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
109
|
+
@Query(returns => [KpiStatistic], {
|
|
110
|
+
description: 'Get aggregated statistics by scope02 (regional level) for dashboard'
|
|
111
|
+
})
|
|
112
|
+
async dashboardRegionalStatistics(
|
|
113
|
+
@Arg('valueDate') valueDate: string,
|
|
114
|
+
@Arg('periodType', type => KpiPeriodType, { defaultValue: KpiPeriodType.MONTH }) periodType: KpiPeriodType,
|
|
115
|
+
@Ctx() context: ResolverContext
|
|
116
|
+
): Promise<KpiStatistic[]> {
|
|
117
|
+
const { domain } = context.state
|
|
118
|
+
|
|
119
|
+
// 지역별로 평균을 내서 하나의 통계로 합침
|
|
120
|
+
const query = `
|
|
121
|
+
SELECT
|
|
122
|
+
'regional-aggregate-' || orgScope.scope02 as id,
|
|
123
|
+
kpi.id as kpiId,
|
|
124
|
+
kpi.name as kpiName,
|
|
125
|
+
orgScope.scope02 as region,
|
|
126
|
+
AVG(stat.mean) as avgMean,
|
|
127
|
+
AVG(stat.median) as avgMedian,
|
|
128
|
+
MIN(stat.minimum) as overallMin,
|
|
129
|
+
MAX(stat.maximum) as overallMax,
|
|
130
|
+
AVG(stat.standardDeviation) as avgStdDev,
|
|
131
|
+
SUM(stat.count) as totalCount,
|
|
132
|
+
COUNT(*) as orgCount
|
|
133
|
+
FROM kpi_statistic stat
|
|
134
|
+
LEFT JOIN kpi ON stat.kpiId = kpi.id
|
|
135
|
+
LEFT JOIN kpi_org_scope orgScope ON stat.kpiOrgScopeId = orgScope.id
|
|
136
|
+
WHERE stat.domainId = ?
|
|
137
|
+
AND stat.periodType = ?
|
|
138
|
+
AND stat.valueDate = ?
|
|
139
|
+
AND orgScope.scope02 IS NOT NULL
|
|
140
|
+
GROUP BY kpi.id, orgScope.scope02
|
|
141
|
+
ORDER BY orgScope.scope02, kpi.name
|
|
142
|
+
`
|
|
143
|
+
|
|
144
|
+
return await getRepository(KpiStatistic).query(query, [domain.id, periodType, valueDate])
|
|
145
|
+
}
|
|
59
146
|
}
|
|
@@ -13,6 +13,7 @@ import { ObjectType, Field, Int, ID } from 'type-graphql'
|
|
|
13
13
|
import { Domain, ScalarObject } from '@things-factory/shell'
|
|
14
14
|
import { User } from '@things-factory/auth-base'
|
|
15
15
|
import { Kpi, KpiPeriodType } from '../kpi/kpi'
|
|
16
|
+
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
|
|
16
17
|
|
|
17
18
|
@Entity()
|
|
18
19
|
@Index('ix_kpi_statistic_target', (kpiStatistic: KpiStatistic) => [kpiStatistic.domain, kpiStatistic.kpi])
|
|
@@ -21,6 +22,18 @@ import { Kpi, KpiPeriodType } from '../kpi/kpi'
|
|
|
21
22
|
kpiStatistic.valueDate,
|
|
22
23
|
kpiStatistic.periodType
|
|
23
24
|
])
|
|
25
|
+
@Index('ix_kpi_statistic_scope', (kpiStatistic: KpiStatistic) => [
|
|
26
|
+
kpiStatistic.domain,
|
|
27
|
+
kpiStatistic.kpi,
|
|
28
|
+
kpiStatistic.kpiOrgScope,
|
|
29
|
+
kpiStatistic.valueDate
|
|
30
|
+
])
|
|
31
|
+
@Index('ix_kpi_statistic_legacy_scope', (kpiStatistic: KpiStatistic) => [
|
|
32
|
+
kpiStatistic.domain,
|
|
33
|
+
kpiStatistic.kpi,
|
|
34
|
+
kpiStatistic.scope,
|
|
35
|
+
kpiStatistic.valueDate
|
|
36
|
+
])
|
|
24
37
|
@ObjectType({
|
|
25
38
|
description:
|
|
26
39
|
'KPI Statistics Entity - Stores comprehensive statistical information for KPIs and Categories including central tendency measures (mean, median), dispersion metrics (standard deviation, variance), range indicators (min, max), and percentile distributions (25th, 75th percentiles, IQR). Supports both KPI and Category targets with flexible period-based aggregation (daily, weekly, monthly, yearly). Includes extensible JSON fields for additional metrics and metadata for calculation tracking.'
|
|
@@ -54,6 +67,25 @@ export class KpiStatistic {
|
|
|
54
67
|
@Field(type => KpiPeriodType, { description: 'Aggregation period type for this statistic.' })
|
|
55
68
|
periodType: KpiPeriodType
|
|
56
69
|
|
|
70
|
+
// === 스코프 정보 ===
|
|
71
|
+
|
|
72
|
+
@ManyToOne(() => KpiOrgScope, { nullable: true })
|
|
73
|
+
@Field(type => KpiOrgScope, {
|
|
74
|
+
nullable: true,
|
|
75
|
+
description: 'Organization scope for scoped statistics. Null for overall statistics.'
|
|
76
|
+
})
|
|
77
|
+
kpiOrgScope?: KpiOrgScope
|
|
78
|
+
|
|
79
|
+
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.kpiOrgScope)
|
|
80
|
+
kpiOrgScopeId?: string
|
|
81
|
+
|
|
82
|
+
@Column({ nullable: true })
|
|
83
|
+
@Field({
|
|
84
|
+
nullable: true,
|
|
85
|
+
description: 'Legacy scope field - use kpiOrgScope instead. Statistical scope - null for overall statistics, category value for scoped statistics (e.g., "서울", "부장", "대규모")'
|
|
86
|
+
})
|
|
87
|
+
scope?: string
|
|
88
|
+
|
|
57
89
|
// === 핵심 통계 필드 (14개) ===
|
|
58
90
|
|
|
59
91
|
// 1. 기본 정보 (3개)
|
|
@@ -6,6 +6,7 @@ import { KpiValue } from './kpi-value'
|
|
|
6
6
|
import { NewKpiValue, KpiValuePatch } from './kpi-value-type'
|
|
7
7
|
import { Kpi } from '../kpi/kpi'
|
|
8
8
|
import { KpiMetric } from '../kpi-metric/kpi-metric'
|
|
9
|
+
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
|
|
9
10
|
import { KpiMetricValue } from '../kpi-metric-value/kpi-metric-value'
|
|
10
11
|
import { KpiPeriodType } from '../kpi/kpi'
|
|
11
12
|
import { getDefaultValueDate } from '../utils/value-date-util'
|
|
@@ -38,18 +39,50 @@ export class KpiValueMutation {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
// KpiOrgScope 처리
|
|
43
|
+
let kpiOrgScope: KpiOrgScope | undefined
|
|
44
|
+
if (kpiValue.kpiOrgScopeId) {
|
|
45
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).findOne({
|
|
46
|
+
where: { id: kpiValue.kpiOrgScopeId, domain: { id: domain.id } }
|
|
47
|
+
})
|
|
48
|
+
if (!kpiOrgScope) {
|
|
49
|
+
throw new Error(`KpiOrgScope not found: ${kpiValue.kpiOrgScopeId}`)
|
|
50
|
+
}
|
|
51
|
+
} else if (kpiValue.org) {
|
|
52
|
+
// Legacy org 처리 - org 문자열로 KpiOrgScope 찾기 또는 생성
|
|
53
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).findOne({
|
|
54
|
+
where: { org: kpiValue.org, domain: { id: domain.id } }
|
|
55
|
+
})
|
|
56
|
+
if (!kpiOrgScope) {
|
|
57
|
+
// 새 KpiOrgScope 생성
|
|
58
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).save({
|
|
59
|
+
entityType: 'Manual',
|
|
60
|
+
entityId: `manual-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
61
|
+
entityName: kpiValue.org,
|
|
62
|
+
org: kpiValue.org,
|
|
63
|
+
domain,
|
|
64
|
+
creator: user,
|
|
65
|
+
updater: user
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
41
70
|
const entity: Partial<KpiValue> = {
|
|
42
71
|
kpi,
|
|
43
72
|
kpiId: kpiValue.kpiId,
|
|
44
73
|
version: kpiValue.version,
|
|
45
74
|
valueDate: kpiValue.valueDate,
|
|
46
75
|
value: kpiValue.value,
|
|
47
|
-
group: kpiValue.group,
|
|
48
76
|
source: kpiValue.source,
|
|
49
77
|
domain: domain,
|
|
50
78
|
creator: user,
|
|
51
79
|
updater: user
|
|
52
80
|
}
|
|
81
|
+
|
|
82
|
+
if (kpiOrgScope) {
|
|
83
|
+
entity.kpiOrgScope = kpiOrgScope
|
|
84
|
+
entity.kpiOrgScopeId = kpiOrgScope.id
|
|
85
|
+
}
|
|
53
86
|
if (inputType) entity.inputType = inputType
|
|
54
87
|
|
|
55
88
|
return await getRepository(KpiValue, tx).save(entity)
|
|
@@ -83,18 +116,50 @@ export class KpiValueMutation {
|
|
|
83
116
|
}
|
|
84
117
|
}
|
|
85
118
|
|
|
119
|
+
// KpiOrgScope 처리
|
|
120
|
+
let kpiOrgScope: KpiOrgScope | undefined
|
|
121
|
+
if (kpiValue.kpiOrgScopeId) {
|
|
122
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).findOne({
|
|
123
|
+
where: { id: kpiValue.kpiOrgScopeId, domain: { id: domain.id } }
|
|
124
|
+
})
|
|
125
|
+
if (!kpiOrgScope) {
|
|
126
|
+
throw new Error(`KpiOrgScope not found: ${kpiValue.kpiOrgScopeId}`)
|
|
127
|
+
}
|
|
128
|
+
} else if (kpiValue.org) {
|
|
129
|
+
// Legacy org 처리
|
|
130
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).findOne({
|
|
131
|
+
where: { org: kpiValue.org, domain: { id: domain.id } }
|
|
132
|
+
})
|
|
133
|
+
if (!kpiOrgScope) {
|
|
134
|
+
// 새 KpiOrgScope 생성
|
|
135
|
+
kpiOrgScope = await getRepository(KpiOrgScope, context.state?.tx).save({
|
|
136
|
+
entityType: 'Manual',
|
|
137
|
+
entityId: `manual-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
138
|
+
entityName: kpiValue.org,
|
|
139
|
+
org: kpiValue.org,
|
|
140
|
+
domain,
|
|
141
|
+
creator: user,
|
|
142
|
+
updater: user
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
86
147
|
const entity: Partial<KpiValue> = {
|
|
87
148
|
kpi,
|
|
88
149
|
kpiId: kpiValue.kpiId,
|
|
89
150
|
version: kpiValue.version || 1,
|
|
90
151
|
valueDate: kpiValue.valueDate,
|
|
91
152
|
value: kpiValue.value,
|
|
92
|
-
group: kpiValue.group || '',
|
|
93
153
|
source: kpiValue.source || 'MANUAL',
|
|
94
154
|
domain: domain,
|
|
95
155
|
creator: user,
|
|
96
156
|
updater: user
|
|
97
157
|
}
|
|
158
|
+
|
|
159
|
+
if (kpiOrgScope) {
|
|
160
|
+
entity.kpiOrgScope = kpiOrgScope
|
|
161
|
+
entity.kpiOrgScopeId = kpiOrgScope.id
|
|
162
|
+
}
|
|
98
163
|
if (inputType) entity.inputType = inputType
|
|
99
164
|
|
|
100
165
|
const savedEntity = await repository.save(entity)
|
|
@@ -276,7 +341,7 @@ export class KpiValueMutation {
|
|
|
276
341
|
// 1. 기존 KPI Value 인스턴스 조회
|
|
277
342
|
const kpiValue = await kpiValueRepo.findOne({
|
|
278
343
|
where: { id, domain: { id: domain.id } },
|
|
279
|
-
relations: ['kpi']
|
|
344
|
+
relations: ['kpi', 'kpiOrgScope']
|
|
280
345
|
})
|
|
281
346
|
if (!kpiValue) throw new Error('KPI Value not found')
|
|
282
347
|
const kpi = kpiValue.kpi || (await kpiRepo.findOne({ where: { id: kpiValue.kpiId, domain: { id: domain.id } } }))
|
|
@@ -284,7 +349,8 @@ export class KpiValueMutation {
|
|
|
284
349
|
if (!kpi.formula) throw new Error('KPI formula 없음')
|
|
285
350
|
const periodType = kpi.periodType || KpiPeriodType.DAY
|
|
286
351
|
const valueDate = kpiValue.valueDate
|
|
287
|
-
const
|
|
352
|
+
const kpiOrgScope = kpiValue.kpiOrgScope
|
|
353
|
+
const org = kpiOrgScope?.entityName || kpiOrgScope?.org || 'unknown' // KpiOrgScope에서 org 정보 추출
|
|
288
354
|
const version = kpiValue.version
|
|
289
355
|
|
|
290
356
|
// 2. formula에서 metric code 추출
|
|
@@ -309,7 +375,7 @@ export class KpiValueMutation {
|
|
|
309
375
|
metric: { id: metric.id },
|
|
310
376
|
valueDate,
|
|
311
377
|
periodType,
|
|
312
|
-
|
|
378
|
+
org: org ?? '',
|
|
313
379
|
domain: { id: domain.id }
|
|
314
380
|
}
|
|
315
381
|
})
|
|
@@ -324,7 +390,7 @@ export class KpiValueMutation {
|
|
|
324
390
|
metric: { id: metric.id },
|
|
325
391
|
valueDate: Between(startDate, endDate),
|
|
326
392
|
periodType: metric.periodType,
|
|
327
|
-
|
|
393
|
+
org: org ?? '',
|
|
328
394
|
domain: { id: domain.id }
|
|
329
395
|
}
|
|
330
396
|
})
|
|
@@ -341,7 +407,7 @@ export class KpiValueMutation {
|
|
|
341
407
|
const ast = parseFormula(kpi.formula)
|
|
342
408
|
const provider = new KpiMetricValueProvider({
|
|
343
409
|
valueDate,
|
|
344
|
-
|
|
410
|
+
org,
|
|
345
411
|
domainId: domain.id,
|
|
346
412
|
tx
|
|
347
413
|
})
|
|
@@ -33,8 +33,11 @@ export class NewKpiValue {
|
|
|
33
33
|
})
|
|
34
34
|
meta?: any
|
|
35
35
|
|
|
36
|
-
@Field({ nullable: true, description: '
|
|
37
|
-
|
|
36
|
+
@Field(type => ID, { nullable: true, description: 'ID of the KPI organization scope for this value.' })
|
|
37
|
+
kpiOrgScopeId?: string
|
|
38
|
+
|
|
39
|
+
@Field({ nullable: true, description: 'Legacy organizational unit identifier (for backward compatibility)' })
|
|
40
|
+
org?: string
|
|
38
41
|
|
|
39
42
|
@Field(type => KpiValueInputType, {
|
|
40
43
|
nullable: true,
|
|
@@ -84,8 +87,11 @@ export class KpiValuePatch {
|
|
|
84
87
|
})
|
|
85
88
|
meta?: any
|
|
86
89
|
|
|
87
|
-
@Field({ nullable: true, description: '
|
|
88
|
-
|
|
90
|
+
@Field(type => ID, { nullable: true, description: 'ID of the KPI organization scope for this value.' })
|
|
91
|
+
kpiOrgScopeId?: string
|
|
92
|
+
|
|
93
|
+
@Field({ nullable: true, description: 'Legacy organizational unit identifier (for backward compatibility)' })
|
|
94
|
+
org?: string
|
|
89
95
|
|
|
90
96
|
@Field(type => KpiValueInputType, {
|
|
91
97
|
nullable: true,
|
|
@@ -14,6 +14,7 @@ import { User } from '@things-factory/auth-base'
|
|
|
14
14
|
import { Domain, ScalarObject, json5Transformer } from '@things-factory/shell'
|
|
15
15
|
import { config } from '@things-factory/env'
|
|
16
16
|
import { Kpi, KpiPeriodType } from '../kpi/kpi'
|
|
17
|
+
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
|
|
17
18
|
|
|
18
19
|
const ORMCONFIG = config.get('ormconfig', {})
|
|
19
20
|
const DATABASE_TYPE = ORMCONFIG.type
|
|
@@ -29,8 +30,8 @@ registerEnumType(KpiValueInputType, {
|
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
@Entity()
|
|
32
|
-
@Index('ix_kpi_value_latest', ['domain', 'kpi', 'valueDate', '
|
|
33
|
-
@Index('ix_kpi_value_latest_for_query', ['domain', 'kpi', 'valueDate', '
|
|
33
|
+
@Index('ix_kpi_value_latest', ['domain', 'kpi', 'valueDate', 'kpiOrgScope', 'version'], { unique: true })
|
|
34
|
+
@Index('ix_kpi_value_latest_for_query', ['domain', 'kpi', 'valueDate', 'kpiOrgScope'], { unique: false })
|
|
34
35
|
@ObjectType({ description: 'Entity for KpiValue' })
|
|
35
36
|
export class KpiValue {
|
|
36
37
|
@PrimaryGeneratedColumn('uuid')
|
|
@@ -78,9 +79,13 @@ export class KpiValue {
|
|
|
78
79
|
})
|
|
79
80
|
score?: number
|
|
80
81
|
|
|
81
|
-
@
|
|
82
|
-
@Field({ nullable: true, description: '
|
|
83
|
-
|
|
82
|
+
@ManyToOne(() => KpiOrgScope, { nullable: true })
|
|
83
|
+
@Field(type => KpiOrgScope, { nullable: true, description: 'Reference to the organization scope mapping for this KPI value.' })
|
|
84
|
+
kpiOrgScope?: KpiOrgScope
|
|
85
|
+
|
|
86
|
+
@RelationId((kpiValue: KpiValue) => kpiValue.kpiOrgScope)
|
|
87
|
+
@Field({ nullable: true, description: 'ID of the referenced KPI organization scope.' })
|
|
88
|
+
kpiOrgScopeId?: string
|
|
84
89
|
|
|
85
90
|
@Column({ nullable: true })
|
|
86
91
|
@Field({
|
|
@@ -9,6 +9,53 @@ export function getISOWeek(date: Date): number {
|
|
|
9
9
|
return weekNo
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function getMonday(year: number, week: number): Date {
|
|
13
|
+
const simple = new Date(year, 0, 1 + (week - 1) * 7)
|
|
14
|
+
const dow = simple.getDay()
|
|
15
|
+
const ISOweekStart = simple
|
|
16
|
+
if (dow <= 4) ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1)
|
|
17
|
+
else ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay())
|
|
18
|
+
return ISOweekStart
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function normalizePeriodDate(valueDate: string, periodType: KpiPeriodType): Date {
|
|
22
|
+
switch (periodType) {
|
|
23
|
+
case KpiPeriodType.DAY:
|
|
24
|
+
return new Date(valueDate + 'T00:00:00.000Z')
|
|
25
|
+
|
|
26
|
+
case KpiPeriodType.WEEK: {
|
|
27
|
+
// "2024-W03" → 해당 주의 월요일
|
|
28
|
+
const [year, week] = valueDate.split('-W')
|
|
29
|
+
return getMonday(parseInt(year), parseInt(week))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
case KpiPeriodType.MONTH:
|
|
33
|
+
return new Date(valueDate + '-01T00:00:00.000Z')
|
|
34
|
+
|
|
35
|
+
case KpiPeriodType.QUARTER: {
|
|
36
|
+
// "2024-Q1" → 해당 분기 시작일
|
|
37
|
+
const [year, quarter] = valueDate.split('-Q')
|
|
38
|
+
const month = (parseInt(quarter) - 1) * 3 + 1
|
|
39
|
+
return new Date(`${year}-${month.toString().padStart(2, '0')}-01T00:00:00.000Z`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
case KpiPeriodType.YEAR:
|
|
43
|
+
return new Date(valueDate + '-01-01T00:00:00.000Z')
|
|
44
|
+
|
|
45
|
+
case KpiPeriodType.RANGE: {
|
|
46
|
+
// "2024-01-01~2024-01-31" → 시작일 사용
|
|
47
|
+
const startDate = valueDate.split('~')[0]
|
|
48
|
+
return new Date(startDate + 'T00:00:00.000Z')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case KpiPeriodType.ALLTIME:
|
|
52
|
+
return new Date('1900-01-01T00:00:00.000Z') // 고정 기준점
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
return new Date(valueDate + 'T00:00:00.000Z')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
12
59
|
export function getDefaultValueDate(periodType: KpiPeriodType, mode: 'current' | 'last' = 'current'): string {
|
|
13
60
|
const now = new Date()
|
|
14
61
|
if (mode === 'last') {
|
package/things-factory.config.js
CHANGED
|
@@ -7,6 +7,7 @@ export default {
|
|
|
7
7
|
{ tagname: 'kpi-overview', page: 'kpi-overview' },
|
|
8
8
|
{ tagname: 'kpi-dashboard', page: 'kpi-dashboard' },
|
|
9
9
|
{ tagname: 'kpi-list-page', page: 'kpi-list' },
|
|
10
|
+
{ tagname: 'kpi-tree-page', page: 'kpi-tree' },
|
|
10
11
|
{ tagname: 'kpi-category-list-page', page: 'kpi-category-list' },
|
|
11
12
|
{ tagname: 'kpi-category-value-list-page', page: 'kpi-category-value-list' },
|
|
12
13
|
{ tagname: 'kpi-metric-list-page', page: 'kpi-metric-list' },
|
package/translations/en.json
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
"label.operators": "operators",
|
|
5
5
|
"label.functions": "functions",
|
|
6
6
|
"title.kpi list": "KPI List",
|
|
7
|
-
"title.kpi
|
|
8
|
-
"title.kpi category value list": "KPI Category Value List",
|
|
7
|
+
"title.kpi tree": "KPI Tree",
|
|
9
8
|
"title.kpi dashboard": "KPI Dashboard",
|
|
10
9
|
"title.kpi value editor": "KPI Value Editor",
|
|
11
10
|
"title.kpi grade list": "KPI Grade List",
|
|
@@ -41,5 +40,18 @@
|
|
|
41
40
|
"field.percentile75": "75th Percentile",
|
|
42
41
|
"field.iqr": "IQR",
|
|
43
42
|
"field.lower_fence": "Lower Fence",
|
|
44
|
-
"field.upper_fence": "Upper Fence"
|
|
43
|
+
"field.upper_fence": "Upper Fence",
|
|
44
|
+
"label.basic_information": "Basic Information",
|
|
45
|
+
"label.is_leaf_node": "Is Leaf Node",
|
|
46
|
+
"label.formula_calculation": "Formula & Calculation",
|
|
47
|
+
"label.kpi_formula": "KPI Formula",
|
|
48
|
+
"label.score_formula": "Score Formula",
|
|
49
|
+
"placeholder.enter_formula": "Enter formula using metric codes (e.g., defect_count / total_count * 100)",
|
|
50
|
+
"placeholder.enter_score_formula": "Enter score calculation formula (e.g., if(value >= 90, 100, value * 0.8))",
|
|
51
|
+
"placeholder.cron_schedule": "0 0 * * * (daily at midnight)",
|
|
52
|
+
"placeholder.timezone": "Asia/Seoul",
|
|
53
|
+
"message.kpi_not_selected": "KPI not selected",
|
|
54
|
+
"message.select_kpi_first": "Please select KPI first.",
|
|
55
|
+
"message.kpis_with_children_cannot_be_deleted": "KPIs with children cannot be deleted.",
|
|
56
|
+
"message.kpi_having_children_cannot_be_deleted": "KPI having children cannot be deleted."
|
|
45
57
|
}
|
package/translations/ja.json
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
"label.operators": "演算子",
|
|
5
5
|
"label.functions": "関数",
|
|
6
6
|
"title.kpi list": "KPIリスト",
|
|
7
|
-
"title.kpi
|
|
8
|
-
"title.kpi category value list": "KPIカテゴリ値リスト",
|
|
7
|
+
"title.kpi tree": "KPIツリー",
|
|
9
8
|
"title.kpi dashboard": "KPIダッシュボード",
|
|
10
9
|
"title.kpi value editor": "KPI値エディタ",
|
|
11
10
|
"title.kpi grade list": "KPIグレードリスト",
|
|
@@ -41,5 +40,16 @@
|
|
|
41
40
|
"field.percentile75": "75パーセンタイル",
|
|
42
41
|
"field.iqr": "四分位範囲",
|
|
43
42
|
"field.lower_fence": "下限フェンス",
|
|
44
|
-
"field.upper_fence": "上限フェンス"
|
|
43
|
+
"field.upper_fence": "上限フェンス",
|
|
44
|
+
"label.basic_information": "基本情報",
|
|
45
|
+
"label.is_leaf_node": "リーフノード",
|
|
46
|
+
"label.formula_calculation": "数式と計算",
|
|
47
|
+
"label.kpi_formula": "KPI数式",
|
|
48
|
+
"label.score_formula": "スコア数式",
|
|
49
|
+
"placeholder.enter_formula": "メトリックコードを使用して数式を入力してください(例:defect_count / total_count * 100)",
|
|
50
|
+
"placeholder.enter_score_formula": "スコア計算数式を入力してください(例:if(value >= 90, 100, value * 0.8))",
|
|
51
|
+
"placeholder.cron_schedule": "0 0 * * *(毎日午前0時)",
|
|
52
|
+
"placeholder.timezone": "Asia/Tokyo",
|
|
53
|
+
"message.kpis_with_children_cannot_be_deleted": "子KPIを持つKPIは削除できません。",
|
|
54
|
+
"message.kpi_having_children_cannot_be_deleted": "子KPIを持つKPIは削除できません。"
|
|
45
55
|
}
|
package/translations/ko.json
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
"label.operators": "연산자",
|
|
5
5
|
"label.functions": "함수",
|
|
6
6
|
"title.kpi list": "KPI 목록",
|
|
7
|
-
"title.kpi
|
|
8
|
-
"title.kpi category value list": "KPI 카테고리 값 목록",
|
|
7
|
+
"title.kpi tree": "KPI 트리",
|
|
9
8
|
"title.kpi dashboard": "KPI 대시보드",
|
|
10
9
|
"title.kpi value editor": "KPI 값 편집기",
|
|
11
10
|
"title.kpi grade list": "KPI 등급 목록",
|
|
@@ -41,5 +40,18 @@
|
|
|
41
40
|
"field.percentile75": "75분위수",
|
|
42
41
|
"field.iqr": "사분위수 범위",
|
|
43
42
|
"field.lower_fence": "하위 울타리",
|
|
44
|
-
"field.upper_fence": "상위 울타리"
|
|
43
|
+
"field.upper_fence": "상위 울타리",
|
|
44
|
+
"label.basic_information": "기본 정보",
|
|
45
|
+
"label.is_leaf_node": "리프 노드",
|
|
46
|
+
"label.formula_calculation": "수식 및 계산",
|
|
47
|
+
"label.kpi_formula": "KPI 수식",
|
|
48
|
+
"label.score_formula": "점수 수식",
|
|
49
|
+
"placeholder.enter_formula": "지표 코드를 사용하여 수식을 입력하세요 (예: defect_count / total_count * 100)",
|
|
50
|
+
"placeholder.enter_score_formula": "점수 계산 수식을 입력하세요 (예: if(value >= 90, 100, value * 0.8))",
|
|
51
|
+
"placeholder.cron_schedule": "0 0 * * * (매일 자정)",
|
|
52
|
+
"placeholder.timezone": "Asia/Seoul",
|
|
53
|
+
"message.kpi_not_selected": "KPI가 선택되지 않음",
|
|
54
|
+
"message.select_kpi_first": "먼저 KPI를 선택하세요.",
|
|
55
|
+
"message.kpis_with_children_cannot_be_deleted": "하위 KPI가 있는 KPI는 삭제할 수 없습니다.",
|
|
56
|
+
"message.kpi_having_children_cannot_be_deleted": "하위 KPI가 있는 KPI는 삭제할 수 없습니다."
|
|
45
57
|
}
|