@things-factory/kpi 10.1.248 → 10.1.249
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/dist-client/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +7 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/charts/kpi-boxplot-chart.d.ts +24 -0
- package/dist-client/charts/kpi-boxplot-chart.js +324 -0
- package/dist-client/charts/kpi-boxplot-chart.js.map +1 -0
- package/dist-client/charts/kpi-mini-trend-chart.d.ts +14 -0
- package/dist-client/charts/kpi-mini-trend-chart.js +148 -0
- package/dist-client/charts/kpi-mini-trend-chart.js.map +1 -0
- package/dist-client/charts/kpi-radar-chart.d.ts +16 -0
- package/dist-client/charts/kpi-radar-chart.js +138 -0
- package/dist-client/charts/kpi-radar-chart.js.map +1 -0
- package/dist-client/charts/kpi-trend-chart.d.ts +25 -0
- package/dist-client/charts/kpi-trend-chart.js +186 -0
- package/dist-client/charts/kpi-trend-chart.js.map +1 -0
- package/dist-client/google-map/common-google-map.d.ts +34 -0
- package/dist-client/google-map/common-google-map.js +333 -0
- package/dist-client/google-map/common-google-map.js.map +1 -0
- package/dist-client/google-map/google-map-loader.d.ts +8 -0
- package/dist-client/google-map/google-map-loader.js +22 -0
- package/dist-client/google-map/google-map-loader.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/kpi/kpi-grade-editor.d.ts +35 -0
- package/dist-client/pages/kpi/kpi-grade-editor.js +165 -0
- package/dist-client/pages/kpi/kpi-grade-editor.js.map +1 -0
- package/dist-client/pages/kpi/kpi-importer.d.ts +23 -0
- package/dist-client/pages/kpi/kpi-importer.js +92 -0
- package/dist-client/pages/kpi/kpi-importer.js.map +1 -0
- package/dist-client/pages/kpi/kpi-list-page.d.ts +136 -0
- package/dist-client/pages/kpi/kpi-list-page.js +948 -0
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -0
- package/dist-client/pages/kpi/kpi-overview-sample.d.ts +36 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js +264 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js.map +1 -0
- package/dist-client/pages/kpi/kpi-overview.d.ts +14 -0
- package/dist-client/pages/kpi/kpi-overview.js +361 -0
- package/dist-client/pages/kpi/kpi-overview.js.map +1 -0
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +51 -0
- package/dist-client/pages/kpi/kpi-tree-page.js +355 -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 +193 -0
- package/dist-client/pages/kpi/kpi-view.js.map +1 -0
- package/dist-client/pages/kpi/kpi-viz-editor.d.ts +19 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js +562 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +17 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +279 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +19 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +385 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +465 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +24 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +440 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +33 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +444 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +369 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +128 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +31 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +493 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +50 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +884 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +146 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +21 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js +160 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.d.ts +23 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js +92 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js.map +1 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +65 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +415 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +52 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +678 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +75 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +64 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +311 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +175 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +35 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +161 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +47 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +655 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.d.ts +23 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js +92 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +65 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +416 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +50 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +700 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.js +92 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +73 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +403 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +47 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/viewparts/kpi-metric-input.d.ts +23 -0
- package/dist-client/viewparts/kpi-metric-input.js +140 -0
- package/dist-client/viewparts/kpi-metric-input.js.map +1 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.d.ts +36 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.js +321 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.js.map +1 -0
- package/dist-client/viewparts/metric-input-hint.d.ts +26 -0
- package/dist-client/viewparts/metric-input-hint.js +19 -0
- package/dist-client/viewparts/metric-input-hint.js.map +1 -0
- package/dist-client/viewparts/metric-input.test.d.ts +1 -0
- package/dist-client/viewparts/metric-input.test.js +43 -0
- package/dist-client/viewparts/metric-input.test.js.map +1 -0
- package/dist-client/viewparts/metric-renderer-registry.d.ts +18 -0
- package/dist-client/viewparts/metric-renderer-registry.js +29 -0
- package/dist-client/viewparts/metric-renderer-registry.js.map +1 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.d.ts +19 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.js +76 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.js.map +1 -0
- package/dist-server/calculator/calculator.test.d.ts +1 -0
- package/dist-server/calculator/calculator.test.js +126 -0
- package/dist-server/calculator/calculator.test.js.map +1 -0
- package/dist-server/calculator/evaluator.d.ts +8 -0
- package/dist-server/calculator/evaluator.js +42 -0
- package/dist-server/calculator/evaluator.js.map +1 -0
- package/dist-server/calculator/functions.d.ts +3 -0
- package/dist-server/calculator/functions.js +62 -0
- package/dist-server/calculator/functions.js.map +1 -0
- package/dist-server/calculator/index.d.ts +6 -0
- package/dist-server/calculator/index.js +10 -0
- package/dist-server/calculator/index.js.map +1 -0
- package/dist-server/calculator/parser.d.ts +34 -0
- package/dist-server/calculator/parser.js +185 -0
- package/dist-server/calculator/parser.js.map +1 -0
- package/dist-server/calculator/provider.d.ts +8 -0
- package/dist-server/calculator/provider.js +13 -0
- package/dist-server/calculator/provider.js.map +1 -0
- package/dist-server/calculator/score.d.ts +21 -0
- package/dist-server/calculator/score.js +36 -0
- package/dist-server/calculator/score.js.map +1 -0
- package/dist-server/calculator/score.test.d.ts +1 -0
- package/dist-server/calculator/score.test.js +47 -0
- package/dist-server/calculator/score.test.js.map +1 -0
- package/dist-server/calculator/validate.d.ts +15 -0
- package/dist-server/calculator/validate.js +30 -0
- package/dist-server/calculator/validate.js.map +1 -0
- package/dist-server/calculator/validate.test.d.ts +1 -0
- package/dist-server/calculator/validate.test.js +45 -0
- package/dist-server/calculator/validate.test.js.map +1 -0
- package/dist-server/controllers/index.d.ts +2 -0
- package/dist-server/controllers/index.js +6 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/controllers/kpi-metric-value-provider.d.ts +11 -0
- package/dist-server/controllers/kpi-metric-value-provider.js +97 -0
- package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -0
- package/dist-server/controllers/kpi-value-provider.d.ts +11 -0
- package/dist-server/controllers/kpi-value-provider.js +50 -0
- package/dist-server/controllers/kpi-value-provider.js.map +1 -0
- package/dist-server/index.d.ts +5 -0
- package/dist-server/index.js +9 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +102 -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/1752192090200-add-meta-to-kpi-metric.d.ts +10 -0
- package/dist-server/migrations/1752192090200-add-meta-to-kpi-metric.js +26 -0
- package/dist-server/migrations/1752192090200-add-meta-to-kpi-metric.js.map +1 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.d.ts +11 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.js +54 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.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/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/migrations/seed-data/kpi-metrics-seed.json +517 -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/routes.d.ts +1 -0
- package/dist-server/routes.js +72 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/index.d.ts +15 -0
- package/dist-server/service/index.js +49 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/kpi/aggregate-kpi.d.ts +8 -0
- package/dist-server/service/kpi/aggregate-kpi.js +123 -0
- package/dist-server/service/kpi/aggregate-kpi.js.map +1 -0
- package/dist-server/service/kpi/event-subscriber.d.ts +9 -0
- package/dist-server/service/kpi/event-subscriber.js +31 -0
- package/dist-server/service/kpi/event-subscriber.js.map +1 -0
- package/dist-server/service/kpi/index.d.ts +8 -0
- package/dist-server/service/kpi/index.js +12 -0
- package/dist-server/service/kpi/index.js.map +1 -0
- package/dist-server/service/kpi/kpi-formula.service.d.ts +49 -0
- package/dist-server/service/kpi/kpi-formula.service.js +165 -0
- package/dist-server/service/kpi/kpi-formula.service.js.map +1 -0
- package/dist-server/service/kpi/kpi-grade.types.d.ts +22 -0
- package/dist-server/service/kpi/kpi-grade.types.js +3 -0
- package/dist-server/service/kpi/kpi-grade.types.js.map +1 -0
- package/dist-server/service/kpi/kpi-history.d.ts +29 -0
- package/dist-server/service/kpi/kpi-history.js +148 -0
- package/dist-server/service/kpi/kpi-history.js.map +1 -0
- package/dist-server/service/kpi/kpi-mutation.d.ts +14 -0
- package/dist-server/service/kpi/kpi-mutation.js +553 -0
- package/dist-server/service/kpi/kpi-mutation.js.map +1 -0
- package/dist-server/service/kpi/kpi-query.d.ts +24 -0
- package/dist-server/service/kpi/kpi-query.js +309 -0
- package/dist-server/service/kpi/kpi-query.js.map +1 -0
- package/dist-server/service/kpi/kpi-type.d.ts +48 -0
- package/dist-server/service/kpi/kpi-type.js +221 -0
- package/dist-server/service/kpi/kpi-type.js.map +1 -0
- package/dist-server/service/kpi/kpi.d.ts +130 -0
- package/dist-server/service/kpi/kpi.js +356 -0
- package/dist-server/service/kpi/kpi.js.map +1 -0
- package/dist-server/service/kpi-alert/index.d.ts +2 -0
- package/dist-server/service/kpi-alert/index.js +6 -0
- package/dist-server/service/kpi-alert/index.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.d.ts +4 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js +72 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.d.ts +8 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js +33 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js.map +1 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.d.ts +8 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +126 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -0
- package/dist-server/service/kpi-metric/index.d.ts +6 -0
- package/dist-server/service/kpi-metric/index.js +10 -0
- package/dist-server/service/kpi-metric/index.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.d.ts +46 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.js +27 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.d.ts +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.js +43 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +10 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js +296 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.d.ts +14 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.js +126 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +45 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.js +177 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric.d.ts +39 -0
- package/dist-server/service/kpi-metric/kpi-metric.js +174 -0
- package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -0
- package/dist-server/service/kpi-metric-value/index.d.ts +6 -0
- package/dist-server/service/kpi-metric-value/index.js +10 -0
- package/dist-server/service/kpi-metric-value/index.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +11 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +283 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +13 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +106 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +26 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +112 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +23 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js +107 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -0
- 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 +140 -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/index.d.ts +6 -0
- package/dist-server/service/kpi-statistic/index.js +10 -0
- package/dist-server/service/kpi-statistic/index.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 +14 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js +279 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.d.ts +17 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js +183 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.d.ts +59 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.js +200 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +42 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js +210 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -0
- package/dist-server/service/kpi-value/index.d.ts +6 -0
- package/dist-server/service/kpi-value/index.js +10 -0
- package/dist-server/service/kpi-value/index.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.d.ts +13 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.js +447 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-query.d.ts +22 -0
- package/dist-server/service/kpi-value/kpi-value-query.js +149 -0
- package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.d.ts +24 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.js +61 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-type.d.ts +33 -0
- package/dist-server/service/kpi-value/kpi-value-type.js +156 -0
- package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value.d.ts +32 -0
- package/dist-server/service/kpi-value/kpi-value.js +175 -0
- package/dist-server/service/kpi-value/kpi-value.js.map +1 -0
- package/dist-server/service/utils/value-date-util.d.ts +4 -0
- package/dist-server/service/utils/value-date-util.js +117 -0
- package/dist-server/service/utils/value-date-util.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.schema = exports.entities = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/* EXPORT ENTITY TYPES */
|
|
6
|
+
tslib_1.__exportStar(require("./kpi-statistic/kpi-statistic"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./kpi/kpi"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./kpi/kpi-type"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./kpi-value/kpi-value"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./kpi-value/kpi-value-type"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./kpi-metric/kpi-metric"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./kpi-metric/kpi-metric-type"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./kpi-metric-value/kpi-metric-value"), exports);
|
|
14
|
+
tslib_1.__exportStar(require("./kpi-metric-value/kpi-metric-value-type"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./kpi-scope/kpi-scope"), exports);
|
|
16
|
+
tslib_1.__exportStar(require("./kpi-org-scope/kpi-org-scope"), exports);
|
|
17
|
+
/* IMPORT ENTITIES AND RESOLVERS */
|
|
18
|
+
const kpi_statistic_1 = require("./kpi-statistic");
|
|
19
|
+
const kpi_1 = require("./kpi");
|
|
20
|
+
const kpi_value_1 = require("./kpi-value");
|
|
21
|
+
const kpi_metric_1 = require("./kpi-metric");
|
|
22
|
+
const kpi_metric_value_1 = require("./kpi-metric-value");
|
|
23
|
+
const kpi_org_scope_1 = require("./kpi-org-scope");
|
|
24
|
+
const kpi_alert_1 = require("./kpi-alert");
|
|
25
|
+
const kpi_scope_1 = require("./kpi-scope");
|
|
26
|
+
exports.entities = [
|
|
27
|
+
/* ENTITIES */
|
|
28
|
+
...kpi_statistic_1.entities,
|
|
29
|
+
...kpi_1.entities,
|
|
30
|
+
...kpi_value_1.entities,
|
|
31
|
+
...kpi_metric_1.entities,
|
|
32
|
+
...kpi_metric_value_1.entities,
|
|
33
|
+
...kpi_org_scope_1.entities,
|
|
34
|
+
...kpi_scope_1.entities
|
|
35
|
+
];
|
|
36
|
+
exports.schema = {
|
|
37
|
+
resolverClasses: [
|
|
38
|
+
/* RESOLVER CLASSES */
|
|
39
|
+
...kpi_statistic_1.resolvers,
|
|
40
|
+
...kpi_1.resolvers,
|
|
41
|
+
...kpi_value_1.resolvers,
|
|
42
|
+
...kpi_metric_1.resolvers,
|
|
43
|
+
...kpi_metric_value_1.resolvers,
|
|
44
|
+
...kpi_org_scope_1.resolvers,
|
|
45
|
+
...kpi_alert_1.resolvers,
|
|
46
|
+
...kpi_scope_1.resolvers
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,wEAA6C;AAC7C,oDAAyB;AACzB,yDAA8B;AAC9B,gEAAqC;AACrC,qEAA0C;AAC1C,kEAAuC;AACvC,uEAA4C;AAC5C,8EAAmD;AACnD,mFAAwD;AACxD,gEAAqC;AACrC,wEAA6C;AAE7C,mCAAmC;AACnC,mDAIwB;AACxB,+BAA0E;AAC1E,2CAA0F;AAC1F,6CAA6F;AAC7F,yDAA6G;AAC7G,mDAAoG;AACpG,2CAA4D;AAC5D,2CAA0F;AAE7E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,wBAAoB;IACvB,GAAG,cAAW;IACd,GAAG,oBAAgB;IACnB,GAAG,qBAAiB;IACpB,GAAG,2BAAsB;IACzB,GAAG,wBAAmB;IACtB,GAAG,oBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,yBAAqB;QACxB,GAAG,eAAY;QACf,GAAG,qBAAiB;QACpB,GAAG,sBAAkB;QACrB,GAAG,4BAAuB;QAC1B,GAAG,yBAAoB;QACvB,GAAG,qBAAiB;QACpB,GAAG,qBAAiB;KACrB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './kpi-statistic/kpi-statistic'\nexport * from './kpi/kpi'\nexport * from './kpi/kpi-type'\nexport * from './kpi-value/kpi-value'\nexport * from './kpi-value/kpi-value-type'\nexport * from './kpi-metric/kpi-metric'\nexport * from './kpi-metric/kpi-metric-type'\nexport * from './kpi-metric-value/kpi-metric-value'\nexport * from './kpi-metric-value/kpi-metric-value-type'\nexport * from './kpi-scope/kpi-scope'\nexport * from './kpi-org-scope/kpi-org-scope'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as KpiStatisticEntities,\n resolvers as KpiStatisticResolvers,\n subscribers as KpiStatisticSubscribers\n} from './kpi-statistic'\nimport { entities as KpiEntities, resolvers as KpiResolvers } from './kpi'\nimport { entities as KpiValueEntities, resolvers as KpiValueResolvers } from './kpi-value'\nimport { entities as KpiMetricEntities, resolvers as KpiMetricResolvers } from './kpi-metric'\nimport { entities as KpiMetricValueEntities, resolvers as KpiMetricValueResolvers } from './kpi-metric-value'\nimport { entities as KpiOrgScopeEntities, resolvers as KpiOrgScopeResolvers } from './kpi-org-scope'\nimport { resolvers as KpiAlertResolvers } from './kpi-alert'\nimport { entities as KpiScopeEntities, resolvers as KpiScopeResolvers } from './kpi-scope'\n\nexport const entities = [\n /* ENTITIES */\n ...KpiStatisticEntities,\n ...KpiEntities,\n ...KpiValueEntities,\n ...KpiMetricEntities,\n ...KpiMetricValueEntities,\n ...KpiOrgScopeEntities,\n ...KpiScopeEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...KpiStatisticResolvers,\n ...KpiResolvers,\n ...KpiValueResolvers,\n ...KpiMetricResolvers,\n ...KpiMetricValueResolvers,\n ...KpiOrgScopeResolvers,\n ...KpiAlertResolvers,\n ...KpiScopeResolvers\n ]\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KPI 단위 집계/산식 자동화 함수
|
|
3
|
+
* @param kpiId KPI ID
|
|
4
|
+
* @param domainId 도메인 ID
|
|
5
|
+
* @param context ResolverContext
|
|
6
|
+
* @returns 저장된 KPI Value 배열
|
|
7
|
+
*/
|
|
8
|
+
export declare function aggregateKpiValue(kpiId: string, domainId: string, context: ResolverContext): Promise<any[]>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aggregateKpiValue = aggregateKpiValue;
|
|
4
|
+
const shell_1 = require("@things-factory/shell");
|
|
5
|
+
const kpi_1 = require("./kpi");
|
|
6
|
+
const kpi_metric_1 = require("../kpi-metric/kpi-metric");
|
|
7
|
+
const kpi_value_1 = require("../kpi-value/kpi-value");
|
|
8
|
+
const kpi_org_scope_1 = require("../kpi-org-scope/kpi-org-scope");
|
|
9
|
+
const parser_js_1 = require("../../calculator/parser.js");
|
|
10
|
+
const evaluator_js_1 = require("../../calculator/evaluator.js");
|
|
11
|
+
const functions_js_1 = require("../../calculator/functions.js");
|
|
12
|
+
const provider_js_1 = require("../../calculator/provider.js");
|
|
13
|
+
const aggregate_kpi_metric_1 = require("../kpi-metric/aggregate-kpi-metric");
|
|
14
|
+
const kpi_value_score_service_1 = require("../kpi-value/kpi-value-score.service");
|
|
15
|
+
/**
|
|
16
|
+
* KPI 단위 집계/산식 자동화 함수
|
|
17
|
+
* @param kpiId KPI ID
|
|
18
|
+
* @param domainId 도메인 ID
|
|
19
|
+
* @param context ResolverContext
|
|
20
|
+
* @returns 저장된 KPI Value 배열
|
|
21
|
+
*/
|
|
22
|
+
async function aggregateKpiValue(kpiId, domainId, context) {
|
|
23
|
+
const tx = context.state?.tx || (0, shell_1.getRepository)(kpi_1.Kpi).manager;
|
|
24
|
+
const scoreService = new kpi_value_score_service_1.KpiValueScoreService();
|
|
25
|
+
// 1. KPI 정보 조회
|
|
26
|
+
const kpi = await (0, shell_1.getRepository)(kpi_1.Kpi).findOne({ where: { id: kpiId, domain: { id: domainId } } });
|
|
27
|
+
if (!kpi)
|
|
28
|
+
throw new Error('KPI 정보 없음');
|
|
29
|
+
if (!kpi.active)
|
|
30
|
+
throw new Error('비활성화된 KPI');
|
|
31
|
+
if (!kpi.formula)
|
|
32
|
+
throw new Error('KPI formula 없음');
|
|
33
|
+
// 2. formula 를 AST 로 파싱하고, 참조 변수명(=metric 이름)을 AST 에서 추출한다.
|
|
34
|
+
// 정규식 추출(extractMetricCodes)은 `[한글 변수]`·공백 포함 이름을 놓치므로 AST 기반이 정확.
|
|
35
|
+
let ast;
|
|
36
|
+
try {
|
|
37
|
+
ast = (0, parser_js_1.parseFormula)(kpi.formula);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
throw new Error(`KPI formula 파싱 실패 (kpi=${kpi.name}): ${e.message}`);
|
|
41
|
+
}
|
|
42
|
+
const metricCodes = (0, parser_js_1.collectVariableNames)(ast);
|
|
43
|
+
const codeValueMap = {};
|
|
44
|
+
for (const code of metricCodes) {
|
|
45
|
+
// 변수명으로 metric 찾기
|
|
46
|
+
const metric = await (0, shell_1.getRepository)(kpi_metric_1.KpiMetric).findOne({ where: { name: code, domain: { id: domainId } } });
|
|
47
|
+
if (!metric)
|
|
48
|
+
throw new Error(`KPI formula metric '${code}' not found`);
|
|
49
|
+
codeValueMap[code] = await (0, aggregate_kpi_metric_1.aggregateKpiMetricValue)(metric.id, domainId, context);
|
|
50
|
+
}
|
|
51
|
+
// org/date/period별로 값 매핑(가장 최근 기준, org key 조합)
|
|
52
|
+
const orgKey = v => [v.date, v.period, v.org].join('|');
|
|
53
|
+
const orgMap = {};
|
|
54
|
+
for (const code of metricCodes) {
|
|
55
|
+
for (const v of codeValueMap[code]) {
|
|
56
|
+
const key = orgKey(v);
|
|
57
|
+
orgMap[key] = orgMap[key] || { ...v, _values: {} };
|
|
58
|
+
orgMap[key]._values[code] = v.value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// formula 계산 — 안전한 AST 평가기 사용 (JS `Function()` eval 제거).
|
|
62
|
+
// · 변수(metric)는 ObjectValueProvider 로 org-combo 값 맵에서 해석
|
|
63
|
+
// · 평가 실패는 조용히 null 로 두지 않고 로그로 남긴다 (원인 추적)
|
|
64
|
+
const savedValues = [];
|
|
65
|
+
for (const key in orgMap) {
|
|
66
|
+
const ctx = orgMap[key]._values;
|
|
67
|
+
let value = null;
|
|
68
|
+
try {
|
|
69
|
+
value = await (0, evaluator_js_1.evaluateFormula)(ast, {
|
|
70
|
+
functions: functions_js_1.builtinFunctions,
|
|
71
|
+
provider: new provider_js_1.ObjectValueProvider(ctx)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error(`[aggregateKpiValue] formula 평가 실패 (kpi=${kpi.name}, formula=${kpi.formula})`, e);
|
|
76
|
+
value = null;
|
|
77
|
+
}
|
|
78
|
+
const valueDate = orgMap[key].date;
|
|
79
|
+
const org = orgMap[key].org;
|
|
80
|
+
const version = kpi.version || 1;
|
|
81
|
+
if (value == null || isNaN(value))
|
|
82
|
+
continue;
|
|
83
|
+
// KpiOrgScope 처리 (org 문자열로 찾기 또는 생성)
|
|
84
|
+
const kpiOrgScopeRepo = (0, shell_1.getRepository)(kpi_org_scope_1.KpiOrgScope, context.state?.tx);
|
|
85
|
+
let kpiOrgScope = await kpiOrgScopeRepo.findOne({
|
|
86
|
+
where: { org: org, domain: { id: domainId } }
|
|
87
|
+
});
|
|
88
|
+
if (!kpiOrgScope) {
|
|
89
|
+
// 새 KpiOrgScope 생성
|
|
90
|
+
kpiOrgScope = await kpiOrgScopeRepo.save({
|
|
91
|
+
entityType: 'AutoAggregate',
|
|
92
|
+
entityId: `auto-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
93
|
+
entityName: org,
|
|
94
|
+
org: org,
|
|
95
|
+
domain: kpi.domain
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// upsert(동일 KPI, valueDate, kpiOrgScope, version) 기준으로 저장
|
|
99
|
+
const repo = (0, shell_1.getRepository)(kpi_value_1.KpiValue, context.state?.tx);
|
|
100
|
+
const existing = await repo.findOne({
|
|
101
|
+
where: { kpi: { id: kpi.id }, valueDate, kpiOrgScope: { id: kpiOrgScope.id }, version, domain: { id: domainId } }
|
|
102
|
+
});
|
|
103
|
+
let entity = existing || repo.create();
|
|
104
|
+
entity.kpi = kpi;
|
|
105
|
+
entity.kpiId = kpi.id;
|
|
106
|
+
entity.version = version;
|
|
107
|
+
entity.valueDate = valueDate;
|
|
108
|
+
entity.value = value;
|
|
109
|
+
entity.kpiOrgScope = kpiOrgScope;
|
|
110
|
+
entity.kpiOrgScopeId = kpiOrgScope.id;
|
|
111
|
+
entity.inputType = kpi_value_1.KpiValueInputType.AUTO;
|
|
112
|
+
entity.source = 'AUTO';
|
|
113
|
+
entity.domain = kpi.domain;
|
|
114
|
+
entity.creator = context.state?.user;
|
|
115
|
+
entity.updater = context.state?.user;
|
|
116
|
+
// 성과 점수 자동 계산 및 저장
|
|
117
|
+
await scoreService.calculateAndSaveScore(entity, kpi);
|
|
118
|
+
entity = await repo.save(entity);
|
|
119
|
+
savedValues.push(entity);
|
|
120
|
+
}
|
|
121
|
+
return savedValues;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=aggregate-kpi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate-kpi.js","sourceRoot":"","sources":["../../../server/service/kpi/aggregate-kpi.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Kpi } from './kpi'\nimport { KpiMetric } from '../kpi-metric/kpi-metric'\nimport { KpiValue, KpiValueInputType } from '../kpi-value/kpi-value'\nimport { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'\nimport { parseFormula, collectVariableNames } from '../../calculator/parser.js'\nimport { evaluateFormula } from '../../calculator/evaluator.js'\nimport { builtinFunctions } from '../../calculator/functions.js'\nimport { ObjectValueProvider } from '../../calculator/provider.js'\nimport { aggregateKpiMetricValue } from '../kpi-metric/aggregate-kpi-metric'\nimport { KpiValueScoreService } from '../kpi-value/kpi-value-score.service'\n\n/**\n * KPI 단위 집계/산식 자동화 함수\n * @param kpiId KPI ID\n * @param domainId 도메인 ID\n * @param context ResolverContext\n * @returns 저장된 KPI Value 배열\n */\nexport async function aggregateKpiValue(kpiId: string, domainId: string, context: ResolverContext) {\n const tx = context.state?.tx || getRepository(Kpi).manager\n const scoreService = new KpiValueScoreService()\n\n // 1. KPI 정보 조회\n const kpi = await getRepository(Kpi).findOne({ where: { id: kpiId, domain: { id: domainId } } })\n if (!kpi) throw new Error('KPI 정보 없음')\n if (!kpi.active) throw new Error('비활성화된 KPI')\n if (!kpi.formula) throw new Error('KPI formula 없음')\n\n // 2. formula 를 AST 로 파싱하고, 참조 변수명(=metric 이름)을 AST 에서 추출한다.\n // 정규식 추출(extractMetricCodes)은 `[한글 변수]`·공백 포함 이름을 놓치므로 AST 기반이 정확.\n let ast\n try {\n ast = parseFormula(kpi.formula)\n } catch (e) {\n throw new Error(`KPI formula 파싱 실패 (kpi=${kpi.name}): ${(e as Error).message}`)\n }\n const metricCodes = collectVariableNames(ast)\n const codeValueMap: Record<string, any[]> = {}\n for (const code of metricCodes) {\n // 변수명으로 metric 찾기\n const metric = await getRepository(KpiMetric).findOne({ where: { name: code, domain: { id: domainId } } })\n if (!metric) throw new Error(`KPI formula metric '${code}' not found`)\n codeValueMap[code] = await aggregateKpiMetricValue(metric.id, domainId, context)\n }\n // org/date/period별로 값 매핑(가장 최근 기준, org key 조합)\n const orgKey = v => [v.date, v.period, v.org].join('|')\n const orgMap: Record<string, any> = {}\n for (const code of metricCodes) {\n for (const v of codeValueMap[code]) {\n const key = orgKey(v)\n orgMap[key] = orgMap[key] || { ...v, _values: {} }\n orgMap[key]._values[code] = v.value\n }\n }\n // formula 계산 — 안전한 AST 평가기 사용 (JS `Function()` eval 제거).\n // · 변수(metric)는 ObjectValueProvider 로 org-combo 값 맵에서 해석\n // · 평가 실패는 조용히 null 로 두지 않고 로그로 남긴다 (원인 추적)\n const savedValues = []\n for (const key in orgMap) {\n const ctx = orgMap[key]._values\n let value = null\n try {\n value = await evaluateFormula(ast, {\n functions: builtinFunctions,\n provider: new ObjectValueProvider(ctx)\n })\n } catch (e) {\n console.error(`[aggregateKpiValue] formula 평가 실패 (kpi=${kpi.name}, formula=${kpi.formula})`, e)\n value = null\n }\n const valueDate = orgMap[key].date\n const org = orgMap[key].org\n const version = kpi.version || 1\n if (value == null || isNaN(value)) continue\n // KpiOrgScope 처리 (org 문자열로 찾기 또는 생성)\n const kpiOrgScopeRepo = getRepository(KpiOrgScope, context.state?.tx)\n let kpiOrgScope = await kpiOrgScopeRepo.findOne({\n where: { org: org, domain: { id: domainId } }\n })\n if (!kpiOrgScope) {\n // 새 KpiOrgScope 생성\n kpiOrgScope = await kpiOrgScopeRepo.save({\n entityType: 'AutoAggregate',\n entityId: `auto-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n entityName: org,\n org: org,\n domain: kpi.domain\n })\n }\n\n // upsert(동일 KPI, valueDate, kpiOrgScope, version) 기준으로 저장\n const repo = getRepository(KpiValue, context.state?.tx)\n const existing = await repo.findOne({\n where: { kpi: { id: kpi.id }, valueDate, kpiOrgScope: { id: kpiOrgScope.id }, version, domain: { id: domainId } }\n })\n let entity = existing || repo.create()\n entity.kpi = kpi\n entity.kpiId = kpi.id\n entity.version = version\n entity.valueDate = valueDate\n entity.value = value\n entity.kpiOrgScope = kpiOrgScope\n entity.kpiOrgScopeId = kpiOrgScope.id\n entity.inputType = KpiValueInputType.AUTO\n entity.source = 'AUTO'\n entity.domain = kpi.domain\n entity.creator = context.state?.user\n entity.updater = context.state?.user\n\n // 성과 점수 자동 계산 및 저장\n await scoreService.calculateAndSaveScore(entity, kpi)\n\n entity = await repo.save(entity)\n savedValues.push(entity)\n }\n return savedValues\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HistoryEntitySubscriber } from '@operato/typeorm-history';
|
|
2
|
+
import { Kpi } from './kpi';
|
|
3
|
+
import { KpiHistory } from './kpi-history';
|
|
4
|
+
export declare class KpiHistoryEntitySubscriber extends HistoryEntitySubscriber<Kpi, KpiHistory> {
|
|
5
|
+
get entity(): typeof Kpi;
|
|
6
|
+
get historyEntity(): typeof KpiHistory;
|
|
7
|
+
afterInsert(event: any): Promise<void>;
|
|
8
|
+
afterUpdate(event: any): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KpiHistoryEntitySubscriber = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
6
|
+
const typeorm_history_1 = require("@operato/typeorm-history");
|
|
7
|
+
const kpi_1 = require("./kpi");
|
|
8
|
+
const kpi_history_1 = require("./kpi-history");
|
|
9
|
+
let KpiHistoryEntitySubscriber = class KpiHistoryEntitySubscriber extends typeorm_history_1.HistoryEntitySubscriber {
|
|
10
|
+
get entity() {
|
|
11
|
+
return kpi_1.Kpi;
|
|
12
|
+
}
|
|
13
|
+
get historyEntity() {
|
|
14
|
+
return kpi_history_1.KpiHistory;
|
|
15
|
+
}
|
|
16
|
+
async afterInsert(event) {
|
|
17
|
+
if (event.entity.state == 'RELEASE') {
|
|
18
|
+
await super.afterInsert(event);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async afterUpdate(event) {
|
|
22
|
+
if (event.entity.state == 'RELEASE') {
|
|
23
|
+
await super.afterUpdate(event);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.KpiHistoryEntitySubscriber = KpiHistoryEntitySubscriber;
|
|
28
|
+
exports.KpiHistoryEntitySubscriber = KpiHistoryEntitySubscriber = tslib_1.__decorate([
|
|
29
|
+
(0, typeorm_1.EventSubscriber)()
|
|
30
|
+
], KpiHistoryEntitySubscriber);
|
|
31
|
+
//# sourceMappingURL=event-subscriber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-subscriber.js","sourceRoot":"","sources":["../../../server/service/kpi/event-subscriber.ts"],"names":[],"mappings":";;;;AAAA,qCAAyC;AAEzC,8DAAkE;AAElE,+BAA2B;AAC3B,+CAA0C;AAGnC,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,yCAAwC;IACtF,IAAW,MAAM;QACf,OAAO,SAAG,CAAA;IACZ,CAAC;IAED,IAAW,aAAa;QACtB,OAAO,wBAAU,CAAA;IACnB,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAK;QAC5B,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;YACpC,MAAM,KAAK,CAAC,WAAW,CAAC,KAAY,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAK;QAC5B,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;YACpC,MAAM,KAAK,CAAC,WAAW,CAAC,KAAY,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;CACF,CAAA;AApBY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,yBAAe,GAAE;GACL,0BAA0B,CAoBtC","sourcesContent":["import { EventSubscriber } from 'typeorm'\n\nimport { HistoryEntitySubscriber } from '@operato/typeorm-history'\n\nimport { Kpi } from './kpi'\nimport { KpiHistory } from './kpi-history'\n\n@EventSubscriber()\nexport class KpiHistoryEntitySubscriber extends HistoryEntitySubscriber<Kpi, KpiHistory> {\n public get entity() {\n return Kpi\n }\n\n public get historyEntity() {\n return KpiHistory\n }\n\n public async afterInsert(event): Promise<void> {\n if (event.entity.state == 'RELEASE') {\n await super.afterInsert(event as any)\n }\n }\n\n public async afterUpdate(event): Promise<void> {\n if (event.entity.state == 'RELEASE') {\n await super.afterUpdate(event as any)\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Kpi } from './kpi';
|
|
2
|
+
import { KpiHistory } from './kpi-history';
|
|
3
|
+
import { KpiHistoryEntitySubscriber } from './event-subscriber';
|
|
4
|
+
import { KpiQuery } from './kpi-query';
|
|
5
|
+
import { KpiMutation } from './kpi-mutation';
|
|
6
|
+
export declare const entities: (typeof Kpi | typeof KpiHistory)[];
|
|
7
|
+
export declare const resolvers: (typeof KpiQuery | typeof KpiMutation)[];
|
|
8
|
+
export declare const subscribers: (typeof KpiHistoryEntitySubscriber)[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.subscribers = exports.resolvers = exports.entities = void 0;
|
|
4
|
+
const kpi_1 = require("./kpi");
|
|
5
|
+
const kpi_history_1 = require("./kpi-history");
|
|
6
|
+
const event_subscriber_1 = require("./event-subscriber");
|
|
7
|
+
const kpi_query_1 = require("./kpi-query");
|
|
8
|
+
const kpi_mutation_1 = require("./kpi-mutation");
|
|
9
|
+
exports.entities = [kpi_1.Kpi, kpi_history_1.KpiHistory];
|
|
10
|
+
exports.resolvers = [kpi_query_1.KpiQuery, kpi_mutation_1.KpiMutation];
|
|
11
|
+
exports.subscribers = [event_subscriber_1.KpiHistoryEntitySubscriber];
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/kpi/index.ts"],"names":[],"mappings":";;;AAAA,+BAA2B;AAC3B,+CAA0C;AAC1C,yDAA+D;AAC/D,2CAAsC;AACtC,iDAA4C;AAE/B,QAAA,QAAQ,GAAG,CAAC,SAAG,EAAE,wBAAU,CAAC,CAAA;AAC5B,QAAA,SAAS,GAAG,CAAC,oBAAQ,EAAE,0BAAW,CAAC,CAAA;AACnC,QAAA,WAAW,GAAG,CAAC,6CAA0B,CAAC,CAAA","sourcesContent":["import { Kpi } from './kpi'\nimport { KpiHistory } from './kpi-history'\nimport { KpiHistoryEntitySubscriber } from './event-subscriber'\nimport { KpiQuery } from './kpi-query'\nimport { KpiMutation } from './kpi-mutation'\n\nexport const entities = [Kpi, KpiHistory]\nexport const resolvers = [KpiQuery, KpiMutation]\nexport const subscribers = [KpiHistoryEntitySubscriber]\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface FormulaValidationResult {
|
|
2
|
+
/** 하드 에러(구문/미지 함수/순환참조)가 없으면 true. 경고는 valid 에 영향 없음. */
|
|
3
|
+
valid: boolean;
|
|
4
|
+
errors: string[];
|
|
5
|
+
warnings: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* KPI formula 파싱/검증/매핑 유효성 검사 서비스
|
|
9
|
+
* - formula에서 Metric 코드(변수명) 추출
|
|
10
|
+
* - 각 Metric의 존재/매핑 유효성 검사
|
|
11
|
+
* - 구문 오류, 미정의 Metric, 매핑 오류 등 에러 리포팅
|
|
12
|
+
*/
|
|
13
|
+
export declare class KpiFormulaService {
|
|
14
|
+
/**
|
|
15
|
+
* formula 문자열에서 Metric 코드(변수명) 추출
|
|
16
|
+
* @param formula KPI formula (예: 'defect_count / total_count * 100')
|
|
17
|
+
* @returns string[] 추출된 Metric 코드 리스트
|
|
18
|
+
*/
|
|
19
|
+
extractMetricCodes(formula: string): string[];
|
|
20
|
+
/**
|
|
21
|
+
* 순수(DB 무관) 구문 검증 — calculator/validate 로 위임 (단위 테스트는 그쪽에서).
|
|
22
|
+
*/
|
|
23
|
+
validateFormulaSyntax(formula: string): import("../../calculator/validate.js").FormulaSyntaxResult;
|
|
24
|
+
/**
|
|
25
|
+
* formula 검증 + metric 참조/순환참조 검사.
|
|
26
|
+
*
|
|
27
|
+
* 정책:
|
|
28
|
+
* - **에러(저장 거부)**: 구문 오류, 미지 함수, 순환참조.
|
|
29
|
+
* - **경고(저장 허용)**: 미정의 metric, 데이터셋 필드 미매핑. (metric 시딩 순서 등 실무 유연성)
|
|
30
|
+
*
|
|
31
|
+
* @returns { valid, errors, warnings } — valid 는 errors 가 없을 때 true (warnings 무관).
|
|
32
|
+
*/
|
|
33
|
+
validateFormula(formula: string, visited?: Set<string>): Promise<FormulaValidationResult>;
|
|
34
|
+
/**
|
|
35
|
+
* 계층 구조 KPI의 값을 자식 KPI들로부터 집계 계산
|
|
36
|
+
* @param kpiId 부모 KPI ID
|
|
37
|
+
* @param valueDate 계산 대상 날짜
|
|
38
|
+
* @param org 조직 단위
|
|
39
|
+
* @returns Promise<number | null> 집계된 값
|
|
40
|
+
*/
|
|
41
|
+
calculateHierarchicalValue(kpiId: string, valueDate: string, org?: string): Promise<number | null>;
|
|
42
|
+
/**
|
|
43
|
+
* KPI가 순환 참조를 생성하는지 확인
|
|
44
|
+
* @param kpiId KPI ID
|
|
45
|
+
* @param parentId 설정하려는 부모 KPI ID
|
|
46
|
+
* @returns Promise<boolean> 순환 참조 여부
|
|
47
|
+
*/
|
|
48
|
+
hasCircularReference(kpiId: string, parentId: string): Promise<boolean>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KpiFormulaService = void 0;
|
|
4
|
+
const shell_1 = require("@things-factory/shell");
|
|
5
|
+
const kpi_metric_1 = require("../kpi-metric/kpi-metric");
|
|
6
|
+
const kpi_1 = require("./kpi");
|
|
7
|
+
const kpi_value_1 = require("../kpi-value/kpi-value");
|
|
8
|
+
const kpi_org_scope_1 = require("../kpi-org-scope/kpi-org-scope");
|
|
9
|
+
const validate_js_1 = require("../../calculator/validate.js");
|
|
10
|
+
/**
|
|
11
|
+
* KPI formula 파싱/검증/매핑 유효성 검사 서비스
|
|
12
|
+
* - formula에서 Metric 코드(변수명) 추출
|
|
13
|
+
* - 각 Metric의 존재/매핑 유효성 검사
|
|
14
|
+
* - 구문 오류, 미정의 Metric, 매핑 오류 등 에러 리포팅
|
|
15
|
+
*/
|
|
16
|
+
class KpiFormulaService {
|
|
17
|
+
/**
|
|
18
|
+
* formula 문자열에서 Metric 코드(변수명) 추출
|
|
19
|
+
* @param formula KPI formula (예: 'defect_count / total_count * 100')
|
|
20
|
+
* @returns string[] 추출된 Metric 코드 리스트
|
|
21
|
+
*/
|
|
22
|
+
extractMetricCodes(formula) {
|
|
23
|
+
// 변수명: 알파벳/언더스코어로 시작, 숫자/알파벳/언더스코어 포함
|
|
24
|
+
const regex = /[a-zA-Z_][a-zA-Z0-9_]*/g;
|
|
25
|
+
// 숫자/예약어/연산자 제외(후처리 필요)
|
|
26
|
+
const reserved = new Set(['if', 'else', 'return', 'true', 'false']);
|
|
27
|
+
return Array.from(new Set((formula.match(regex) || []).filter(code => isNaN(Number(code)) && !reserved.has(code))));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 순수(DB 무관) 구문 검증 — calculator/validate 로 위임 (단위 테스트는 그쪽에서).
|
|
31
|
+
*/
|
|
32
|
+
validateFormulaSyntax(formula) {
|
|
33
|
+
return (0, validate_js_1.validateFormulaSyntax)(formula);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* formula 검증 + metric 참조/순환참조 검사.
|
|
37
|
+
*
|
|
38
|
+
* 정책:
|
|
39
|
+
* - **에러(저장 거부)**: 구문 오류, 미지 함수, 순환참조.
|
|
40
|
+
* - **경고(저장 허용)**: 미정의 metric, 데이터셋 필드 미매핑. (metric 시딩 순서 등 실무 유연성)
|
|
41
|
+
*
|
|
42
|
+
* @returns { valid, errors, warnings } — valid 는 errors 가 없을 때 true (warnings 무관).
|
|
43
|
+
*/
|
|
44
|
+
async validateFormula(formula, visited = new Set()) {
|
|
45
|
+
// 1) 순수 구문/함수 검증 — 하드 에러면 즉시 실패
|
|
46
|
+
const { errors, warnings, variables } = this.validateFormulaSyntax(formula);
|
|
47
|
+
if (errors.length)
|
|
48
|
+
return { valid: false, errors, warnings };
|
|
49
|
+
// 2) metric 참조 검증 (DB) — 미존재/미매핑은 경고, 순환참조는 에러
|
|
50
|
+
const metricRepo = (0, shell_1.getRepository)(kpi_metric_1.KpiMetric);
|
|
51
|
+
for (const code of variables) {
|
|
52
|
+
if (visited.has(code)) {
|
|
53
|
+
errors.push(`순환참조 감지: ${Array.from(visited).join(' → ')} → ${code}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
visited.add(code);
|
|
57
|
+
const metric = await metricRepo.findOne({ where: { name: code } });
|
|
58
|
+
if (!metric) {
|
|
59
|
+
warnings.push(`Metric '${code}' 미정의 (경고 — 저장은 허용)`);
|
|
60
|
+
visited.delete(code);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!metric.dataSetId || !metric.fieldName) {
|
|
64
|
+
warnings.push(`Metric '${code}' 데이터셋 필드 미매핑 (경고)`);
|
|
65
|
+
}
|
|
66
|
+
// metric 이 자체 formula 를 가지면 재귀 검증(순환참조 포함)
|
|
67
|
+
if (metric.formula) {
|
|
68
|
+
const sub = await this.validateFormula(metric.formula, new Set(visited));
|
|
69
|
+
errors.push(...sub.errors);
|
|
70
|
+
warnings.push(...sub.warnings);
|
|
71
|
+
}
|
|
72
|
+
visited.delete(code);
|
|
73
|
+
}
|
|
74
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 계층 구조 KPI의 값을 자식 KPI들로부터 집계 계산
|
|
78
|
+
* @param kpiId 부모 KPI ID
|
|
79
|
+
* @param valueDate 계산 대상 날짜
|
|
80
|
+
* @param org 조직 단위
|
|
81
|
+
* @returns Promise<number | null> 집계된 값
|
|
82
|
+
*/
|
|
83
|
+
async calculateHierarchicalValue(kpiId, valueDate, org) {
|
|
84
|
+
const kpiRepo = (0, shell_1.getRepository)(kpi_1.Kpi);
|
|
85
|
+
const kpi = await kpiRepo.findOne({
|
|
86
|
+
where: { id: kpiId },
|
|
87
|
+
relations: ['children']
|
|
88
|
+
});
|
|
89
|
+
if (!kpi || !kpi.children || kpi.children.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const childValues = [];
|
|
93
|
+
const childWeights = [];
|
|
94
|
+
for (const child of kpi.children) {
|
|
95
|
+
let childValue = null;
|
|
96
|
+
if (child.isLeaf) {
|
|
97
|
+
// Leaf KPI인 경우 KpiValue에서 실제 값을 가져옴
|
|
98
|
+
const kpiValueRepo = (0, shell_1.getRepository)(kpi_value_1.KpiValue);
|
|
99
|
+
let kpiValue = null;
|
|
100
|
+
if (org) {
|
|
101
|
+
const kpiOrgScope = await (0, shell_1.getRepository)(kpi_org_scope_1.KpiOrgScope).findOne({
|
|
102
|
+
where: { org: org, domain: { id: child.domain?.id } }
|
|
103
|
+
});
|
|
104
|
+
if (kpiOrgScope) {
|
|
105
|
+
kpiValue = await kpiValueRepo.findOne({
|
|
106
|
+
where: {
|
|
107
|
+
kpi: { id: child.id },
|
|
108
|
+
valueDate,
|
|
109
|
+
kpiOrgScope: { id: kpiOrgScope.id }
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// org가 없는 경우 kpiOrgScope 없이 조회
|
|
116
|
+
kpiValue = await kpiValueRepo.findOne({
|
|
117
|
+
where: {
|
|
118
|
+
kpi: { id: child.id },
|
|
119
|
+
valueDate,
|
|
120
|
+
kpiOrgScope: null
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
childValue = kpiValue?.value || null;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// 중간 노드인 경우 재귀적으로 계산
|
|
128
|
+
childValue = await this.calculateHierarchicalValue(child.id, valueDate, org);
|
|
129
|
+
}
|
|
130
|
+
if (childValue !== null) {
|
|
131
|
+
childValues.push(childValue);
|
|
132
|
+
childWeights.push(child.weight || 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (childValues.length === 0) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
// 가중 평균 계산
|
|
139
|
+
const totalWeight = childWeights.reduce((sum, weight) => sum + weight, 0);
|
|
140
|
+
const weightedSum = childValues.reduce((sum, value, index) => sum + value * childWeights[index], 0);
|
|
141
|
+
return totalWeight > 0 ? weightedSum / totalWeight : null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* KPI가 순환 참조를 생성하는지 확인
|
|
145
|
+
* @param kpiId KPI ID
|
|
146
|
+
* @param parentId 설정하려는 부모 KPI ID
|
|
147
|
+
* @returns Promise<boolean> 순환 참조 여부
|
|
148
|
+
*/
|
|
149
|
+
async hasCircularReference(kpiId, parentId) {
|
|
150
|
+
if (kpiId === parentId) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
const kpiRepo = (0, shell_1.getRepository)(kpi_1.Kpi);
|
|
154
|
+
const parent = await kpiRepo.findOne({
|
|
155
|
+
where: { id: parentId },
|
|
156
|
+
relations: ['parent']
|
|
157
|
+
});
|
|
158
|
+
if (!parent || !parent.parent) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return await this.hasCircularReference(kpiId, parent.parent.id);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.KpiFormulaService = KpiFormulaService;
|
|
165
|
+
//# sourceMappingURL=kpi-formula.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kpi-formula.service.js","sourceRoot":"","sources":["../../../server/service/kpi/kpi-formula.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { KpiMetric } from '../kpi-metric/kpi-metric'\nimport { Kpi } from './kpi'\nimport { KpiValue } from '../kpi-value/kpi-value'\nimport { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'\nimport { validateFormulaSyntax } from '../../calculator/validate.js'\n\nexport interface FormulaValidationResult {\n /** 하드 에러(구문/미지 함수/순환참조)가 없으면 true. 경고는 valid 에 영향 없음. */\n valid: boolean\n errors: string[]\n warnings: string[]\n}\n\n/**\n * KPI formula 파싱/검증/매핑 유효성 검사 서비스\n * - formula에서 Metric 코드(변수명) 추출\n * - 각 Metric의 존재/매핑 유효성 검사\n * - 구문 오류, 미정의 Metric, 매핑 오류 등 에러 리포팅\n */\nexport class KpiFormulaService {\n /**\n * formula 문자열에서 Metric 코드(변수명) 추출\n * @param formula KPI formula (예: 'defect_count / total_count * 100')\n * @returns string[] 추출된 Metric 코드 리스트\n */\n extractMetricCodes(formula: string): string[] {\n // 변수명: 알파벳/언더스코어로 시작, 숫자/알파벳/언더스코어 포함\n const regex = /[a-zA-Z_][a-zA-Z0-9_]*/g\n // 숫자/예약어/연산자 제외(후처리 필요)\n const reserved = new Set(['if', 'else', 'return', 'true', 'false'])\n return Array.from(new Set((formula.match(regex) || []).filter(code => isNaN(Number(code)) && !reserved.has(code))))\n }\n\n /**\n * 순수(DB 무관) 구문 검증 — calculator/validate 로 위임 (단위 테스트는 그쪽에서).\n */\n validateFormulaSyntax(formula: string) {\n return validateFormulaSyntax(formula)\n }\n\n /**\n * formula 검증 + metric 참조/순환참조 검사.\n *\n * 정책:\n * - **에러(저장 거부)**: 구문 오류, 미지 함수, 순환참조.\n * - **경고(저장 허용)**: 미정의 metric, 데이터셋 필드 미매핑. (metric 시딩 순서 등 실무 유연성)\n *\n * @returns { valid, errors, warnings } — valid 는 errors 가 없을 때 true (warnings 무관).\n */\n async validateFormula(formula: string, visited: Set<string> = new Set()): Promise<FormulaValidationResult> {\n // 1) 순수 구문/함수 검증 — 하드 에러면 즉시 실패\n const { errors, warnings, variables } = this.validateFormulaSyntax(formula)\n if (errors.length) return { valid: false, errors, warnings }\n\n // 2) metric 참조 검증 (DB) — 미존재/미매핑은 경고, 순환참조는 에러\n const metricRepo = getRepository(KpiMetric)\n for (const code of variables) {\n if (visited.has(code)) {\n errors.push(`순환참조 감지: ${Array.from(visited).join(' → ')} → ${code}`)\n continue\n }\n visited.add(code)\n const metric = await metricRepo.findOne({ where: { name: code } })\n if (!metric) {\n warnings.push(`Metric '${code}' 미정의 (경고 — 저장은 허용)`)\n visited.delete(code)\n continue\n }\n if (!metric.dataSetId || !metric.fieldName) {\n warnings.push(`Metric '${code}' 데이터셋 필드 미매핑 (경고)`)\n }\n // metric 이 자체 formula 를 가지면 재귀 검증(순환참조 포함)\n if ((metric as any).formula) {\n const sub = await this.validateFormula((metric as any).formula, new Set(visited))\n errors.push(...sub.errors)\n warnings.push(...sub.warnings)\n }\n visited.delete(code)\n }\n return { valid: errors.length === 0, errors, warnings }\n }\n\n /**\n * 계층 구조 KPI의 값을 자식 KPI들로부터 집계 계산\n * @param kpiId 부모 KPI ID\n * @param valueDate 계산 대상 날짜\n * @param org 조직 단위\n * @returns Promise<number | null> 집계된 값\n */\n async calculateHierarchicalValue(kpiId: string, valueDate: string, org?: string): Promise<number | null> {\n const kpiRepo = getRepository(Kpi)\n const kpi = await kpiRepo.findOne({\n where: { id: kpiId },\n relations: ['children']\n })\n\n if (!kpi || !kpi.children || kpi.children.length === 0) {\n return null\n }\n\n const childValues: number[] = []\n const childWeights: number[] = []\n\n for (const child of kpi.children) {\n let childValue: number | null = null\n \n if (child.isLeaf) {\n // Leaf KPI인 경우 KpiValue에서 실제 값을 가져옴\n const kpiValueRepo = getRepository(KpiValue)\n let kpiValue: KpiValue | null = null\n if (org) {\n const kpiOrgScope = await getRepository(KpiOrgScope).findOne({\n where: { org: org, domain: { id: child.domain?.id } }\n })\n if (kpiOrgScope) {\n kpiValue = await kpiValueRepo.findOne({ \n where: { \n kpi: { id: child.id }, \n valueDate,\n kpiOrgScope: { id: kpiOrgScope.id }\n }\n })\n }\n } else {\n // org가 없는 경우 kpiOrgScope 없이 조회 \n kpiValue = await kpiValueRepo.findOne({ \n where: { \n kpi: { id: child.id }, \n valueDate,\n kpiOrgScope: null\n }\n })\n }\n childValue = kpiValue?.value || null\n } else {\n // 중간 노드인 경우 재귀적으로 계산\n childValue = await this.calculateHierarchicalValue(child.id, valueDate, org)\n }\n\n if (childValue !== null) {\n childValues.push(childValue)\n childWeights.push(child.weight || 1)\n }\n }\n\n if (childValues.length === 0) {\n return null\n }\n\n // 가중 평균 계산\n const totalWeight = childWeights.reduce((sum, weight) => sum + weight, 0)\n const weightedSum = childValues.reduce((sum, value, index) => sum + value * childWeights[index], 0)\n \n return totalWeight > 0 ? weightedSum / totalWeight : null\n }\n\n /**\n * KPI가 순환 참조를 생성하는지 확인\n * @param kpiId KPI ID\n * @param parentId 설정하려는 부모 KPI ID\n * @returns Promise<boolean> 순환 참조 여부\n */\n async hasCircularReference(kpiId: string, parentId: string): Promise<boolean> {\n if (kpiId === parentId) {\n return true\n }\n\n const kpiRepo = getRepository(Kpi)\n const parent = await kpiRepo.findOne({\n where: { id: parentId },\n relations: ['parent']\n })\n\n if (!parent || !parent.parent) {\n return false\n }\n\n return await this.hasCircularReference(kpiId, parent.parent.id)\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KPI 성과 점수 lookup table 타입 정의
|
|
3
|
+
* 복잡한 성과 점수 변환을 수식으로 표현하기 어려운 경우 사용
|
|
4
|
+
*/
|
|
5
|
+
export interface KpiScore {
|
|
6
|
+
/** 성과 지수명 (예: A, B, C, 우수, 양호 등) */
|
|
7
|
+
name: string;
|
|
8
|
+
/** 성과 점수 변환 기준 최소값 */
|
|
9
|
+
minValue: number;
|
|
10
|
+
/** 성과 점수 변환 기준 최대값 */
|
|
11
|
+
maxValue: number;
|
|
12
|
+
/** 성과 지수 점수 (선택사항) */
|
|
13
|
+
score?: number;
|
|
14
|
+
/** 성과 지수 색상 (선택사항) */
|
|
15
|
+
color?: string;
|
|
16
|
+
/** 성과 지수 설명 (선택사항) */
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* KPI 성과 점수 lookup table 배열 타입
|
|
21
|
+
*/
|
|
22
|
+
export type KpiScores = KpiScore[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kpi-grade.types.js","sourceRoot":"","sources":["../../../server/service/kpi/kpi-grade.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * KPI 성과 점수 lookup table 타입 정의\n * 복잡한 성과 점수 변환을 수식으로 표현하기 어려운 경우 사용\n */\nexport interface KpiScore {\n /** 성과 지수명 (예: A, B, C, 우수, 양호 등) */\n name: string\n\n /** 성과 점수 변환 기준 최소값 */\n minValue: number\n\n /** 성과 점수 변환 기준 최대값 */\n maxValue: number\n\n /** 성과 지수 점수 (선택사항) */\n score?: number\n\n /** 성과 지수 색상 (선택사항) */\n color?: string\n\n /** 성과 지수 설명 (선택사항) */\n description?: string\n}\n\n/**\n * KPI 성과 점수 lookup table 배열 타입\n */\nexport type KpiScores = KpiScore[]\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HistoryActionType, HistoryEntityInterface } from '@operato/typeorm-history';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { Domain } from '@things-factory/shell';
|
|
4
|
+
import { Kpi, KpiStatus, KpiPeriodType } from './kpi';
|
|
5
|
+
import { KpiScores } from './kpi-grade.types';
|
|
6
|
+
export declare class KpiHistory implements HistoryEntityInterface<Kpi> {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
version?: number;
|
|
9
|
+
domain?: Domain;
|
|
10
|
+
domainId?: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
formula?: string;
|
|
14
|
+
active?: boolean;
|
|
15
|
+
state?: KpiStatus;
|
|
16
|
+
thumbnail?: string;
|
|
17
|
+
grades?: KpiScores;
|
|
18
|
+
weight?: number;
|
|
19
|
+
periodType: KpiPeriodType;
|
|
20
|
+
createdAt?: Date;
|
|
21
|
+
updatedAt?: Date;
|
|
22
|
+
deletedAt?: Date;
|
|
23
|
+
creator?: User;
|
|
24
|
+
creatorId?: string;
|
|
25
|
+
updater?: User;
|
|
26
|
+
updaterId?: string;
|
|
27
|
+
originalId: string;
|
|
28
|
+
action: HistoryActionType;
|
|
29
|
+
}
|