@things-factory/ops-master 10.1.35 → 10.1.37
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 +1 -0
- package/dist-client/bootstrap.js +26 -0
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/master-change-policy-axes.d.ts +26 -0
- package/dist-client/master-change-policy-axes.js +18 -0
- package/dist-client/master-change-policy-axes.js.map +1 -0
- package/dist-client/master-change-policy-panel.d.ts +49 -0
- package/dist-client/master-change-policy-panel.js +393 -0
- package/dist-client/master-change-policy-panel.js.map +1 -0
- package/dist-client/pages/master/applied-when-dialog.d.ts +45 -0
- package/dist-client/pages/master/applied-when-dialog.js +139 -0
- package/dist-client/pages/master/applied-when-dialog.js.map +1 -0
- package/dist-client/pages/master/assignment-fields.d.ts +27 -0
- package/dist-client/pages/master/assignment-fields.js +62 -0
- package/dist-client/pages/master/assignment-fields.js.map +1 -0
- package/dist-client/pages/master/equipment.js +1 -0
- package/dist-client/pages/master/equipment.js.map +1 -1
- package/dist-client/pages/master/master-page-base.d.ts +12 -0
- package/dist-client/pages/master/master-page-base.js +38 -4
- package/dist-client/pages/master/master-page-base.js.map +1 -1
- package/dist-client/pages/master/operational-location.js +1 -0
- package/dist-client/pages/master/operational-location.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/approval-route.d.ts +37 -0
- package/dist-server/service/approval-route.js +34 -0
- package/dist-server/service/approval-route.js.map +1 -0
- package/dist-server/service/assignment/applied-when.d.ts +24 -0
- package/dist-server/service/assignment/applied-when.js +16 -0
- package/dist-server/service/assignment/applied-when.js.map +1 -0
- package/dist-server/service/assignment/assignment-history.d.ts +92 -0
- package/dist-server/service/assignment/assignment-history.js +289 -0
- package/dist-server/service/assignment/assignment-history.js.map +1 -0
- package/dist-server/service/assignment/assignment-write.d.ts +42 -0
- package/dist-server/service/assignment/assignment-write.js +101 -0
- package/dist-server/service/assignment/assignment-write.js.map +1 -0
- package/dist-server/service/assignment/master-value-claim.d.ts +135 -0
- package/dist-server/service/assignment/master-value-claim.js +174 -0
- package/dist-server/service/assignment/master-value-claim.js.map +1 -0
- package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -1
- package/dist-server/service/equipment/equipment-master-resolver.js +6 -3
- package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
- package/dist-server/service/index.d.ts +6 -1
- package/dist-server/service/index.js +10 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/location/location-master-resolver.d.ts +4 -1
- package/dist-server/service/location/location-master-resolver.js +6 -3
- package/dist-server/service/location/location-master-resolver.js.map +1 -1
- package/dist-server/service/master-change-policy.d.ts +65 -2
- package/dist-server/service/master-change-policy.js +166 -21
- package/dist-server/service/master-change-policy.js.map +1 -1
- package/dist-server/service/master-crud.d.ts +11 -0
- package/dist-server/service/master-crud.js +12 -1
- package/dist-server/service/master-crud.js.map +1 -1
- package/dist-server/service/material/material-revision-service.js +13 -0
- package/dist-server/service/material/material-revision-service.js.map +1 -1
- package/dist-server/service/recipe/recipe-revision-service.js +13 -0
- package/dist-server/service/recipe/recipe-revision-service.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-revision-service.js +14 -0
- package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -4
- package/translations/en.json +59 -2
- package/translations/ja.json +58 -1
- package/translations/ko.json +59 -2
- package/translations/ms.json +58 -1
- package/translations/zh.json +60 -1
|
@@ -15,6 +15,7 @@ tslib_1.__exportStar(require("./test-spec/test-spec-master-resolver"), exports);
|
|
|
15
15
|
tslib_1.__exportStar(require("./master-crud"), exports);
|
|
16
16
|
tslib_1.__exportStar(require("./master-axis"), exports);
|
|
17
17
|
tslib_1.__exportStar(require("./master-change-policy"), exports);
|
|
18
|
+
tslib_1.__exportStar(require("./approval-route"), exports);
|
|
18
19
|
tslib_1.__exportStar(require("./master-conformance"), exports);
|
|
19
20
|
tslib_1.__exportStar(require("./master-conformance-resolver"), exports);
|
|
20
21
|
tslib_1.__exportStar(require("./master-list"), exports);
|
|
@@ -84,10 +85,16 @@ const status_1 = require("./status");
|
|
|
84
85
|
/* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */
|
|
85
86
|
const master_conformance_resolver_1 = require("./master-conformance-resolver");
|
|
86
87
|
const master_change_policy_1 = require("./master-change-policy");
|
|
88
|
+
const master_value_claim_1 = require("./assignment/master-value-claim");
|
|
87
89
|
const test_spec_1 = require("./test-spec");
|
|
88
90
|
exports.entities = [
|
|
89
91
|
/* ENTITIES */
|
|
90
92
|
master_change_policy_1.MasterChangePolicy,
|
|
93
|
+
/*
|
|
94
|
+
* 배정 이력. **등록이 빠져 있었다**(2026-09-23 확인) — 표가 안 서니 이력을 쓰는 길이 실제
|
|
95
|
+
* 설치본에는 없었다. 시험 하네스가 자기 목록에 직접 넣어서 초록이었다.
|
|
96
|
+
*/
|
|
97
|
+
master_value_claim_1.MasterValueClaim,
|
|
91
98
|
...personnel_1.entities,
|
|
92
99
|
...quality_1.entities,
|
|
93
100
|
...equipment_1.entities,
|
|
@@ -119,4 +126,7 @@ exports.schema = {
|
|
|
119
126
|
master_conformance_resolver_1.MasterConformanceResolver
|
|
120
127
|
]
|
|
121
128
|
};
|
|
129
|
+
tslib_1.__exportStar(require("./assignment/master-value-claim"), exports);
|
|
130
|
+
tslib_1.__exportStar(require("./assignment/assignment-history"), exports);
|
|
131
|
+
tslib_1.__exportStar(require("./assignment/assignment-write"), exports);
|
|
122
132
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,oEAAyC;AACzC,gFAAqD;AACrD,gFAAqD;AACrD,wEAA6C;AAC7C,8EAAmD;AACnD,8EAAmD;AACnD,gFAAqD;AACrD,0EAA+C;AAC/C,gFAAqD;AACrD,wDAA6B;AAC7B,wDAA6B;AAC7B,iEAAsC;AACtC,+DAAoC;AACpC,wEAA6C;AAC7C,wDAA6B;AAC7B,6DAAkC;AAClC,sEAA2C;AAC3C,uEAA4C;AAC5C,sEAA2C;AAC3C,oEAAyC;AACzC,gEAAqC;AACrC,sEAA2C;AAC3C,yEAA8C;AAC9C,kEAAuC;AACvC,uEAA4C;AAC5C,sEAA2C;AAC3C,kEAAuC;AACvC,iEAAsC;AACtC,uEAA4C;AAC5C,+DAAoC;AACpC,8DAAmC;AACnC,0EAA+C;AAC/C,qEAA0C;AAC1C,oEAAyC;AACzC,oEAAyC;AACzC,yEAA8C;AAC9C,uEAA4C;AAC5C,+EAAoD;AACpD,gFAAqD;AACrD,6EAAkD;AAClD,qEAA0C;AAC1C,oEAAyC;AACzC,2EAAgD;AAChD,uEAA4C;AAC5C,sEAA2C;AAC3C,0EAA+C;AAC/C,mFAAwD;AACxD,qEAA0C;AAC1C,mEAAwC;AACxC,wEAA6C;AAC7C,2EAAgD;AAChD,2EAAgD;AAChD,4EAAiD;AACjD,yEAA8C;AAC9C,oEAAyC;AACzC,iEAAsC;AACtC,gEAAqC;AACrC,yEAA8C;AAC9C,yEAA8C;AAC9C,iFAAsD;AACtD,kFAAuD;AACvD,+EAAoD;AACpD,uEAA4C;AAC5C,sEAA2C;AAC3C,8DAAmC;AACnC,iEAAsC;AACtC,yEAA8C;AAE9C,mCAAmC;AACnC,mCAAgF;AAChF,2CAA4F;AAC5F,yCAAyF;AACzF,yCAAyF;AACzF,2CAA4F;AAC5F,2CAA4F;AAC5F,uCAAsF;AACtF,wEAAwE;AACxE,qCAAmF;AACnF,qCAAuD;AACvD,uCAAuC;AACvC,+EAAyE;AACzE,iEAAuF;AACvF,2CAA0F;AAE7E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,yCAAkB;IAClB,GAAG,oBAAiB;IACpB,GAAG,kBAAe;IAClB,GAAG,oBAAiB;IACpB,GAAG,gBAAa;IAChB,GAAG,mBAAgB;IACnB,GAAG,mBAAgB;IACnB,GAAG,oBAAiB;IACpB,GAAG,iBAAc;IACjB,GAAG,oBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,iDAA0B;QAC1B,GAAG,qBAAkB;QACrB,GAAG,mBAAgB;QACnB,GAAG,qBAAkB;QACrB,GAAG,iBAAc;QACjB,GAAG,oBAAiB;QACpB,GAAG,oBAAiB;QACpB,GAAG,qBAAkB;QACrB,GAAG,kBAAe;QAClB,GAAG,qBAAiB;QACpB,GAAG,kBAAe;QAElB;;;WAGG;QACH,uDAAyB;KAC1B;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './resource/resource-class'\nexport * from './personnel/personnel-master-resolver'\nexport * from './equipment/equipment-master-resolver'\nexport * from './asset/asset-master-resolver'\nexport * from './material/material-master-resolver'\nexport * from './location/location-master-resolver'\nexport * from './operation/operation-master-resolver'\nexport * from './recipe/recipe-master-resolver'\nexport * from './test-spec/test-spec-master-resolver'\nexport * from './master-crud'\nexport * from './master-axis'\nexport * from './master-change-policy'\nexport * from './master-conformance'\nexport * from './master-conformance-resolver'\nexport * from './master-list'\nexport * from './personnel/person'\nexport * from './personnel/personnel-class'\nexport * from './personnel/personnel-master'\nexport * from './personnel/personnel-query'\nexport * from './personnel/qualification'\nexport * from './equipment/equipment'\nexport * from './equipment/equipment-class'\nexport * from './equipment/state-period-rules'\nexport * from './equipment/stop-reason'\nexport * from './equipment/equipment-master'\nexport * from './equipment/equipment-query'\nexport * from './asset/asset-lifecycle'\nexport * from './asset/physical-asset'\nexport * from './asset/physical-asset-class'\nexport * from './asset/asset-master'\nexport * from './asset/asset-query'\nexport * from './location/operational-location'\nexport * from './location/location-master'\nexport * from './location/location-query'\nexport * from './material/material-class'\nexport * from './material/material-definition'\nexport * from './material/material-revision'\nexport * from './material/material-revision-service'\nexport * from './material/material-revision-resolver'\nexport * from './material/material-effective-list'\nexport * from './material/material-master'\nexport * from './material/material-query'\nexport * from './operation/operation-definition'\nexport * from './operation/operation-master'\nexport * from './operation/operation-query'\nexport * from './operation/operation-readiness'\nexport * from './operation/operation-readiness-resolver'\nexport * from './recipe/recipe-definition'\nexport * from './recipe/recipe-revision'\nexport * from './recipe/recipe-revision-line'\nexport * from './recipe/recipe-revision-content'\nexport * from './recipe/recipe-revision-service'\nexport * from './recipe/recipe-revision-resolver'\nexport * from './recipe/recipe-effective-list'\nexport * from './recipe/route-definition'\nexport * from './recipe/recipe-master'\nexport * from './recipe/recipe-query'\nexport * from './test-spec/test-specification'\nexport * from './test-spec/test-spec-revision'\nexport * from './test-spec/test-spec-revision-service'\nexport * from './test-spec/test-spec-revision-resolver'\nexport * from './test-spec/test-spec-effective-list'\nexport * from './test-spec/test-spec-master'\nexport * from './test-spec/test-spec-query'\nexport * from './status/axis-stamp'\nexport * from './status/master-status'\nexport * from './quality/nonconformance-cause'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AssetEntities, resolvers as AssetResolvers } from './asset'\nimport { entities as EquipmentEntities, resolvers as EquipmentResolvers } from './equipment'\nimport { entities as LocationEntities, resolvers as LocationResolvers } from './location'\nimport { entities as MaterialEntities, resolvers as MaterialResolvers } from './material'\nimport { entities as OperationEntities, resolvers as OperationResolvers } from './operation'\nimport { entities as PersonnelEntities, resolvers as PersonnelResolvers } from './personnel'\nimport { entities as QualityEntities, resolvers as QualityResolvers } from './quality'\n/* 라우팅·레시피 — 품목→지시서를 이으려면 그 사이에 이 둘이 있어야 한다(§`recipe-definition.ts`). */\nimport { entities as RecipeEntities, resolvers as RecipeResolvers } from './recipe'\nimport { resolvers as StatusResolvers } from './status'\n/* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */\nimport { MasterConformanceResolver } from './master-conformance-resolver'\nimport { MasterChangePolicy, MasterChangePolicyResolver } from './master-change-policy'\nimport { entities as TestSpecEntities, resolvers as TestSpecResolvers } from './test-spec'\n\nexport const entities = [\n /* ENTITIES */\n MasterChangePolicy,\n ...PersonnelEntities,\n ...QualityEntities,\n ...EquipmentEntities,\n ...AssetEntities,\n ...LocationEntities,\n ...MaterialEntities,\n ...OperationEntities,\n ...RecipeEntities,\n ...TestSpecEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n MasterChangePolicyResolver,\n ...PersonnelResolvers,\n ...QualityResolvers,\n ...EquipmentResolvers,\n ...AssetResolvers,\n ...LocationResolvers,\n ...MaterialResolvers,\n ...OperationResolvers,\n ...RecipeResolvers,\n ...TestSpecResolvers,\n ...StatusResolvers,\n\n /*\n * **파일을 export 하는 것만으로는 스키마에 안 올라온다.** 여기 적어야 한다 — 처음에 그것을\n * 빠뜨려 `masterConformance` 가 「Cannot query field」로 나왔다. [확인함] 2026-09-04\n */\n MasterConformanceResolver\n ]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,oEAAyC;AACzC,gFAAqD;AACrD,gFAAqD;AACrD,wEAA6C;AAC7C,8EAAmD;AACnD,8EAAmD;AACnD,gFAAqD;AACrD,0EAA+C;AAC/C,gFAAqD;AACrD,wDAA6B;AAC7B,wDAA6B;AAC7B,iEAAsC;AACtC,2DAAgC;AAChC,+DAAoC;AACpC,wEAA6C;AAC7C,wDAA6B;AAC7B,6DAAkC;AAClC,sEAA2C;AAC3C,uEAA4C;AAC5C,sEAA2C;AAC3C,oEAAyC;AACzC,gEAAqC;AACrC,sEAA2C;AAC3C,yEAA8C;AAC9C,kEAAuC;AACvC,uEAA4C;AAC5C,sEAA2C;AAC3C,kEAAuC;AACvC,iEAAsC;AACtC,uEAA4C;AAC5C,+DAAoC;AACpC,8DAAmC;AACnC,0EAA+C;AAC/C,qEAA0C;AAC1C,oEAAyC;AACzC,oEAAyC;AACzC,yEAA8C;AAC9C,uEAA4C;AAC5C,+EAAoD;AACpD,gFAAqD;AACrD,6EAAkD;AAClD,qEAA0C;AAC1C,oEAAyC;AACzC,2EAAgD;AAChD,uEAA4C;AAC5C,sEAA2C;AAC3C,0EAA+C;AAC/C,mFAAwD;AACxD,qEAA0C;AAC1C,mEAAwC;AACxC,wEAA6C;AAC7C,2EAAgD;AAChD,2EAAgD;AAChD,4EAAiD;AACjD,yEAA8C;AAC9C,oEAAyC;AACzC,iEAAsC;AACtC,gEAAqC;AACrC,yEAA8C;AAC9C,yEAA8C;AAC9C,iFAAsD;AACtD,kFAAuD;AACvD,+EAAoD;AACpD,uEAA4C;AAC5C,sEAA2C;AAC3C,8DAAmC;AACnC,iEAAsC;AACtC,yEAA8C;AAE9C,mCAAmC;AACnC,mCAAgF;AAChF,2CAA4F;AAC5F,yCAAyF;AACzF,yCAAyF;AACzF,2CAA4F;AAC5F,2CAA4F;AAC5F,uCAAsF;AACtF,wEAAwE;AACxE,qCAAmF;AACnF,qCAAuD;AACvD,uCAAuC;AACvC,+EAAyE;AACzE,iEAAuF;AACvF,wEAAkE;AAClE,2CAA0F;AAE7E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,yCAAkB;IAClB;;;OAGG;IACH,qCAAgB;IAChB,GAAG,oBAAiB;IACpB,GAAG,kBAAe;IAClB,GAAG,oBAAiB;IACpB,GAAG,gBAAa;IAChB,GAAG,mBAAgB;IACnB,GAAG,mBAAgB;IACnB,GAAG,oBAAiB;IACpB,GAAG,iBAAc;IACjB,GAAG,oBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,iDAA0B;QAC1B,GAAG,qBAAkB;QACrB,GAAG,mBAAgB;QACnB,GAAG,qBAAkB;QACrB,GAAG,iBAAc;QACjB,GAAG,oBAAiB;QACpB,GAAG,oBAAiB;QACpB,GAAG,qBAAkB;QACrB,GAAG,kBAAe;QAClB,GAAG,qBAAiB;QACpB,GAAG,kBAAe;QAElB;;;WAGG;QACH,uDAAyB;KAC1B;CACF,CAAA;AACD,0EAA+C;AAC/C,0EAA+C;AAC/C,wEAA6C","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './resource/resource-class'\nexport * from './personnel/personnel-master-resolver'\nexport * from './equipment/equipment-master-resolver'\nexport * from './asset/asset-master-resolver'\nexport * from './material/material-master-resolver'\nexport * from './location/location-master-resolver'\nexport * from './operation/operation-master-resolver'\nexport * from './recipe/recipe-master-resolver'\nexport * from './test-spec/test-spec-master-resolver'\nexport * from './master-crud'\nexport * from './master-axis'\nexport * from './master-change-policy'\nexport * from './approval-route'\nexport * from './master-conformance'\nexport * from './master-conformance-resolver'\nexport * from './master-list'\nexport * from './personnel/person'\nexport * from './personnel/personnel-class'\nexport * from './personnel/personnel-master'\nexport * from './personnel/personnel-query'\nexport * from './personnel/qualification'\nexport * from './equipment/equipment'\nexport * from './equipment/equipment-class'\nexport * from './equipment/state-period-rules'\nexport * from './equipment/stop-reason'\nexport * from './equipment/equipment-master'\nexport * from './equipment/equipment-query'\nexport * from './asset/asset-lifecycle'\nexport * from './asset/physical-asset'\nexport * from './asset/physical-asset-class'\nexport * from './asset/asset-master'\nexport * from './asset/asset-query'\nexport * from './location/operational-location'\nexport * from './location/location-master'\nexport * from './location/location-query'\nexport * from './material/material-class'\nexport * from './material/material-definition'\nexport * from './material/material-revision'\nexport * from './material/material-revision-service'\nexport * from './material/material-revision-resolver'\nexport * from './material/material-effective-list'\nexport * from './material/material-master'\nexport * from './material/material-query'\nexport * from './operation/operation-definition'\nexport * from './operation/operation-master'\nexport * from './operation/operation-query'\nexport * from './operation/operation-readiness'\nexport * from './operation/operation-readiness-resolver'\nexport * from './recipe/recipe-definition'\nexport * from './recipe/recipe-revision'\nexport * from './recipe/recipe-revision-line'\nexport * from './recipe/recipe-revision-content'\nexport * from './recipe/recipe-revision-service'\nexport * from './recipe/recipe-revision-resolver'\nexport * from './recipe/recipe-effective-list'\nexport * from './recipe/route-definition'\nexport * from './recipe/recipe-master'\nexport * from './recipe/recipe-query'\nexport * from './test-spec/test-specification'\nexport * from './test-spec/test-spec-revision'\nexport * from './test-spec/test-spec-revision-service'\nexport * from './test-spec/test-spec-revision-resolver'\nexport * from './test-spec/test-spec-effective-list'\nexport * from './test-spec/test-spec-master'\nexport * from './test-spec/test-spec-query'\nexport * from './status/axis-stamp'\nexport * from './status/master-status'\nexport * from './quality/nonconformance-cause'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AssetEntities, resolvers as AssetResolvers } from './asset'\nimport { entities as EquipmentEntities, resolvers as EquipmentResolvers } from './equipment'\nimport { entities as LocationEntities, resolvers as LocationResolvers } from './location'\nimport { entities as MaterialEntities, resolvers as MaterialResolvers } from './material'\nimport { entities as OperationEntities, resolvers as OperationResolvers } from './operation'\nimport { entities as PersonnelEntities, resolvers as PersonnelResolvers } from './personnel'\nimport { entities as QualityEntities, resolvers as QualityResolvers } from './quality'\n/* 라우팅·레시피 — 품목→지시서를 이으려면 그 사이에 이 둘이 있어야 한다(§`recipe-definition.ts`). */\nimport { entities as RecipeEntities, resolvers as RecipeResolvers } from './recipe'\nimport { resolvers as StatusResolvers } from './status'\n/* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */\nimport { MasterConformanceResolver } from './master-conformance-resolver'\nimport { MasterChangePolicy, MasterChangePolicyResolver } from './master-change-policy'\nimport { MasterValueClaim } from './assignment/master-value-claim'\nimport { entities as TestSpecEntities, resolvers as TestSpecResolvers } from './test-spec'\n\nexport const entities = [\n /* ENTITIES */\n MasterChangePolicy,\n /*\n * 배정 이력. **등록이 빠져 있었다**(2026-09-23 확인) — 표가 안 서니 이력을 쓰는 길이 실제\n * 설치본에는 없었다. 시험 하네스가 자기 목록에 직접 넣어서 초록이었다.\n */\n MasterValueClaim,\n ...PersonnelEntities,\n ...QualityEntities,\n ...EquipmentEntities,\n ...AssetEntities,\n ...LocationEntities,\n ...MaterialEntities,\n ...OperationEntities,\n ...RecipeEntities,\n ...TestSpecEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n MasterChangePolicyResolver,\n ...PersonnelResolvers,\n ...QualityResolvers,\n ...EquipmentResolvers,\n ...AssetResolvers,\n ...LocationResolvers,\n ...MaterialResolvers,\n ...OperationResolvers,\n ...RecipeResolvers,\n ...TestSpecResolvers,\n ...StatusResolvers,\n\n /*\n * **파일을 export 하는 것만으로는 스키마에 안 올라온다.** 여기 적어야 한다 — 처음에 그것을\n * 빠뜨려 `masterConformance` 가 「Cannot query field」로 나왔다. [확인함] 2026-09-04\n */\n MasterConformanceResolver\n ]\n}\nexport * from './assignment/master-value-claim'\nexport * from './assignment/assignment-history'\nexport * from './assignment/assignment-write'\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AppliedWhen } from '../assignment/applied-when';
|
|
1
2
|
import { ListParam } from '@things-factory/shell';
|
|
2
3
|
import { MasterPatch } from '../master-crud';
|
|
3
4
|
import { OperationalLocation } from './operational-location';
|
|
@@ -53,5 +54,7 @@ export declare class OperationalLocationResolver {
|
|
|
53
54
|
* 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
|
|
54
55
|
* 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
|
|
55
56
|
*/
|
|
56
|
-
updateMultipleOperationalLocation(patches: MasterPatch[], context: ResolverContext
|
|
57
|
+
updateMultipleOperationalLocation(patches: MasterPatch[], context: ResolverContext,
|
|
58
|
+
/** **언제부터 그러한가** — 근무조 선언과 부모 관계를 고칠 때만 온다(§`applied-when`). */
|
|
59
|
+
applied?: AppliedWhen): Promise<OperationalLocation[]>;
|
|
57
60
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.OperationalLocationResolver = exports.OperationalLocationList = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const applied_when_1 = require("../assignment/applied-when");
|
|
6
7
|
const shell_1 = require("@things-factory/shell");
|
|
7
8
|
const type_graphql_2 = require("type-graphql");
|
|
8
9
|
const shell_2 = require("@things-factory/shell");
|
|
@@ -234,11 +235,12 @@ let OperationalLocationResolver = class OperationalLocationResolver {
|
|
|
234
235
|
* 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
|
|
235
236
|
* 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
|
|
236
237
|
*/
|
|
237
|
-
async updateMultipleOperationalLocation(patches, context) {
|
|
238
|
+
async updateMultipleOperationalLocation(patches, context, applied) {
|
|
238
239
|
const { domain, user, tx } = context.state;
|
|
239
240
|
const axis = (0, master_axis_1.masterAxisOf)('operationalLocation');
|
|
240
241
|
const saved = await (0, master_crud_1.saveMasterPatches)(tx, domain, axis.entity, patches, user, {
|
|
241
|
-
hasEffectivePeriod: axis.hasEffectivePeriod
|
|
242
|
+
hasEffectivePeriod: axis.hasEffectivePeriod,
|
|
243
|
+
...(0, applied_when_1.appliedOptions)(applied)
|
|
242
244
|
});
|
|
243
245
|
return saved;
|
|
244
246
|
}
|
|
@@ -299,8 +301,9 @@ tslib_1.__decorate([
|
|
|
299
301
|
(0, type_graphql_2.Mutation)(returns => [operational_location_1.OperationalLocation], { description: 'To create, change or retire operational locations' }),
|
|
300
302
|
tslib_1.__param(0, (0, type_graphql_2.Arg)('patches', type => [shell_2.ScalarObject])),
|
|
301
303
|
tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
|
|
304
|
+
tslib_1.__param(2, (0, type_graphql_2.Arg)('applied', type => shell_2.ScalarObject, { nullable: true })),
|
|
302
305
|
tslib_1.__metadata("design:type", Function),
|
|
303
|
-
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
306
|
+
tslib_1.__metadata("design:paramtypes", [Array, Object, Object]),
|
|
304
307
|
tslib_1.__metadata("design:returntype", Promise)
|
|
305
308
|
], OperationalLocationResolver.prototype, "updateMultipleOperationalLocation", null);
|
|
306
309
|
exports.OperationalLocationResolver = OperationalLocationResolver = tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location-master-resolver.js","sourceRoot":"","sources":["../../../server/service/location/location-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { OperationalLocation } from './operational-location'\nimport { Person } from '../personnel/person'\nimport { Equipment } from '../equipment/equipment'\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { cappedList, indexReferrers } from '../resource/referrers'\n\n/**\n * 자리 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of operational locations' })\nexport class OperationalLocationList {\n @Field(type => [OperationalLocation])\n items: OperationalLocation[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * 「바로 아래에 몇이 있나」를 세는 곳.\n *\n * ── 물어본 요청만 센다 ──────────────────────────────────────────────────────\n * 처음에는 목록 리졸버가 늘 세게 했다. 그러면 **개수를 쓰지 않는 조회도 값을 치른다** — 고르기 팝업,\n * 자동완성, 다른 화면의 목록까지 전부. 로케이션 목록은 여러 곳에서 부른다.\n *\n * 그래서 `childCount` 를 필드 리졸버로 둔다. GraphQL 이 **물어본 요청에만** 이것을 부르므로, 안 묻는\n * 조회는 질의가 한 번뿐이다. 「이 목록은 개수가 필요하다/아니다」를 서버 쪽에 플래그로 두지 않아도\n * 된다 — 묻는 것이 곧 그 표시다.\n *\n * ── 물었으면 한 번에 센다 ───────────────────────────────────────────────────\n * 필드 리졸버는 줄마다 불린다. 거기서 그때그때 세면 `limit 20` 에 카운트 20회, 100 이면 100회다 —\n * N+1 이다.\n *\n * 그래서 목록 리졸버가 그 페이지를 요청에 남겨 두고(`locationPage`), 첫 줄이 물을 때 **페이지 전체를\n * 한 번에** 세어 요청 안에 담아 둔다. 담는 것은 **결과가 아니라 약속(Promise)** 이다 — 줄들이 거의\n * 동시에 묻기 때문에, 결과를 담으면 스무 개가 각자 질의를 띄운 뒤에야 채워진다.\n *\n * SELECT parent_id, COUNT(1) … WHERE domain = ? AND parent_id IN (…) GROUP BY parent_id\n *\n * `IN` 에 들어가는 것은 그 페이지의 아이디와 이름뿐이라 표 전체 크기와 무관하다.\n * `ix_ops_operational_location_1`(도메인 · 상위)이 이 질의를 받는다.\n *\n * ── 상위를 두 가지로 찾는다 ──────────────────────────────────────────────────\n * `parentId` 는 외래 키가 아니라 문자열이라 쓰는 쪽이 정한다. 지금 데이터는 아이디가 아니라 **이름**을\n * 담고 있다(`LINE-1` 의 상위가 `PLANT-1`). 한쪽만 보면 개수가 오류 보고 없이 0 이 된다.\n */\nasync function countChildren(locations: OperationalLocation[], domain: Domain): Promise<Map<string, number>> {\n const keys = [...new Set(locations.flatMap(location => [location.id, location.name]).filter(Boolean))]\n\n if (!keys.length) return new Map()\n\n const counted = await getRepository(OperationalLocation)\n .createQueryBuilder('location')\n .select('location.parentId', 'parentId')\n .addSelect('COUNT(1)', 'count')\n .where('location.domain = :domain', { domain: domain.id })\n .andWhere('location.parentId IN (:...keys)', { keys })\n .groupBy('location.parentId')\n .getRawMany()\n\n return new Map<string, number>(counted.map(row => [row.parentId, Number(row.count)]))\n}\n\n/**\n * 아이디로 적힌 자식과 이름으로 적힌 자식을 **더한다.**\n *\n * 같은 자식이 두 규약으로 중복 등록돼 있으면 개수가 부풀 수 있다. 그런 데이터는 이미 결함이지만, 여기서\n * 한쪽을 조용히 버리지는 않는다 — 버리면 어느 쪽이 빠졌는지 아무도 모른다.\n */\nfunction sumFor(location: OperationalLocation, counted: Map<string, number>): number {\n return (counted.get(location.id) || 0) + (location.name ? counted.get(location.name) || 0 : 0)\n}\n\n/**\n * **이 자리에 무엇이 있나** — 들어오는 관계.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 자리 하나를 열어도 「여기서 누가 일하나 · 무슨 설비가 있나」를 알 수 없었다. 사람 화면을 열어\n * 근무 자리로 걸러야 했고, 설비는 또 설비 화면에서 같은 일을 해야 했다. 다른 화면의 참조 칸을\n * 눌러 자리로 걸어 들어와도 그 자리가 무엇인지는 이름뿐이었다.\n *\n * ── 줄마다 세지 않는다 ─────────────────────────────────────────────────────\n * `childCount` 와 같은 방식이다. 목록 리졸버가 그 페이지를 요청에 남겨 두고, 첫 줄이 물을 때\n * **페이지 전체를 한 번에** 묻는다. 담는 것은 결과가 아니라 **약속**이다 — 줄들이 거의 동시에\n * 물으므로 결과를 담으면 스무 개가 각자 질의를 띄운다.\n *\n * ── 자리를 두 가지로 찾는다 ────────────────────────────────────────────────\n * `homeLocation` 은 외래 키가 아니라 문자열이라 쓰는 쪽이 정한다. 지금 자료는 **이름**을 담는다\n * (`P-ECU-S-0001` 의 근무 자리가 `ECU-SUB-01`). 아이디도 함께 넣어 묻는다 — 한쪽만 보면 목록이\n * 오류 보고 없이 빈다. `childCount` 가 같은 이유로 그렇게 한다.\n */\n\n/** 한 자리에 보일 이름 수. 넘치면 몇이 남았는지 함께 낸다. */\nconst OCCUPANT_SHOWN = 8\n\ninterface OccupantScan {\n staff: Map<string, string[]>\n equipment: Map<string, string[]>\n assets: Map<string, string[]>\n}\n\nasync function scanOccupants(locations: OperationalLocation[], domain: Domain): Promise<OccupantScan> {\n const keys = [...new Set(locations.flatMap(one => [one.id, one.name]).filter(Boolean))] as string[]\n\n if (keys.length === 0) {\n return { staff: new Map(), equipment: new Map(), assets: new Map() }\n }\n\n const of = async (Entity: any) => {\n const rows = await getRepository(Entity)\n .createQueryBuilder('row')\n .select(['row.id', 'row.name', 'row.homeLocation'])\n .where('row.domain_id = :domain', { domain: domain.id })\n .andWhere('row.home_location IN (:...keys)', { keys })\n .getMany()\n\n return indexReferrers((rows as any[]).map(one => ({ points: one.homeLocation, name: one.name })))\n }\n\n const [staff, equipment, assets] = await Promise.all([of(Person), of(Equipment), of(PhysicalAsset)])\n\n return { staff, equipment, assets }\n}\n\n/**\n * 이 줄의 답을 낸다.\n *\n * **페이지에 없으면 그 줄만 따로 묻는다.** 한 요청에 목록 조회가 둘 있으면 뒤엣것이 앞엣것의\n * 페이지를 덮어쓰고, 그때 남는 줄들이 「아무도 없다」로 나온다 — 자리가 비어 있는 것과 못 본 것이\n * 같아 보인다. 들어오는 관계는 **비어 있는 것이 기본**이라 이 거짓말이 특히 안 띈다.\n * 틀린 목록보다 질의 한 번이 낫다. `childCount` 가 같은 이유로 같은 자리를 둔다.\n */\nasync function occupantsOf(location: OperationalLocation, context: ResolverContext): Promise<OccupantScan> {\n const state = context.state as {\n domain: Domain\n locationPage?: OperationalLocation[]\n occupantScan?: Promise<OccupantScan>\n }\n\n const page = state.locationPage\n const inPage = !!page?.some(one => one.id === location.id)\n\n if (!inPage) {\n return scanOccupants([location], state.domain)\n }\n\n /* 요청 안에서 한 번만 훑는다. 약속을 담는다. */\n state.occupantScan = state.occupantScan || scanOccupants(page!, state.domain)\n\n return state.occupantScan\n}\n\n/**\n * 한 자리의 이름들을 낸다 — 아이디로도 이름으로도 걸린 것을 합친다.\n *\n * 자료가 어느 쪽으로 적혔는지 자리마다 다를 수 있어 둘 다 본다. 합칠 때 겹치는 것은 한 번만 센다.\n */\nfunction namesAt(index: Map<string, string[]>, location: OperationalLocation): string[] {\n const found = [...(index.get(location.id) ?? []), ...(index.get(location.name) ?? [])]\n\n return [...new Set(found)].sort()\n}\n\n@Resolver(OperationalLocation)\n@UseMiddleware(SpeakRefusals)\nexport class OperationalLocationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => OperationalLocationList, { description: 'To fetch operational locations' })\n async operationalLocations(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OperationalLocationList> {\n const { domain } = context.state\n const { items, total } = await listMaster('operationalLocation', params, domain)\n\n const locations = items as OperationalLocation[]\n\n /*\n * 여기서 개수를 세지 않는다. 이 페이지만 요청에 남겨 둔다 — `childCount` 를 **물어본 요청**이\n * 있으면 그 리졸버가 이것을 보고 한 번에 센다(위 머리말).\n */\n context.state.locationPage = locations\n\n return { items: locations, total }\n }\n\n /**\n * 이 자리 바로 아래에 몇이 있나.\n *\n * 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 아이콘만으로는 그것을\n * 말하지 못한다 — 눌러 보기 전에는 안에 무엇이 있는지, 있기는 한지 알 수 없다.\n *\n * 세는 방식과 값은 `countChildren` 머리말에 적었다.\n */\n /**\n * 여기서 일하는 사람들.\n *\n * **넘치면 몇이 남았는지 말한다**(`occupantOverflow`). 조용히 자르면 「셋이 있다」와 「서른이\n * 있는데 셋만 보인다」가 같아 보이고, 그 칸을 보고 사람이 판단을 내린다.\n *\n * 아무도 없으면 빈 목록이다 — 없는 것은 없다고 낸다.\n */\n @FieldResolver(type => [String], { description: 'People whose home location is here' })\n async staff(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).staff, location), OCCUPANT_SHOWN).shown\n }\n\n /** 여기 있는 설비들. */\n @FieldResolver(type => [String], { description: 'Equipment stationed here' })\n async equipmentHere(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).equipment, location), OCCUPANT_SHOWN).shown\n }\n\n /** 여기 있는 자산들 — 공구 · 지그처럼 옮겨 다니는 것. */\n @FieldResolver(type => [String], { description: 'Physical assets kept here' })\n async assetsHere(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).assets, location), OCCUPANT_SHOWN).shown\n }\n\n /**\n * 위 세 칸에서 **안 보인 것이 몇인가.**\n *\n * 셋을 합쳐 하나로 낸다. 칸마다 따로 두면 화면이 숫자 셋을 더 들고 있어야 하는데, 사람이 알고\n * 싶은 것은 「이 자리에 더 있나」 하나다.\n */\n @FieldResolver(type => Int, { description: 'How many occupants are not shown in the lists above' })\n async occupantOverflow(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<number> {\n const scan = await occupantsOf(location, context)\n\n return (\n cappedList(namesAt(scan.staff, location), OCCUPANT_SHOWN).hidden +\n cappedList(namesAt(scan.equipment, location), OCCUPANT_SHOWN).hidden +\n cappedList(namesAt(scan.assets, location), OCCUPANT_SHOWN).hidden\n )\n }\n\n @FieldResolver(type => Int, { description: 'How many locations sit directly under this one' })\n async childCount(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<number> {\n const { domain, locationPage } = context.state as { domain: Domain; locationPage?: OperationalLocation[] }\n\n const page = locationPage as OperationalLocation[] | undefined\n const inPage = !!page?.some(one => one.id === location.id)\n\n /*\n * 목록 조회가 남긴 페이지에 없으면 그 줄만 센다.\n *\n * 한 요청에 목록 조회가 둘 있으면 뒤엣것이 앞엣것의 페이지를 덮어쓴다. 그때 남는 줄들을 조용히 0 으로\n * 두지 않는다 — 틀린 개수보다 질의 한 번이 낫다.\n */\n if (!inPage) {\n const counted = await countChildren([location], domain)\n return sumFor(location, counted)\n }\n\n /*\n * 요청 안에서 한 번만 센다. **약속을 담는다** — 줄들이 거의 동시에 묻기 때문에 결과를 담으면\n * 스무 개가 각자 질의를 띄운다.\n */\n const state = context.state as { locationChildCounts?: Promise<Map<string, number>> }\n state.locationChildCounts = state.locationChildCounts || countChildren(page!, domain)\n\n return sumFor(location, await state.locationChildCounts)\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [OperationalLocation], { description: 'To create, change or retire operational locations' })\n async updateMultipleOperationalLocation(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<OperationalLocation[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('operationalLocation')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as OperationalLocation[]\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"location-master-resolver.js","sourceRoot":"","sources":["../../../server/service/location/location-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { AppliedWhen, appliedOptions } from '../assignment/applied-when'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { OperationalLocation } from './operational-location'\nimport { Person } from '../personnel/person'\nimport { Equipment } from '../equipment/equipment'\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { cappedList, indexReferrers } from '../resource/referrers'\n\n/**\n * 자리 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of operational locations' })\nexport class OperationalLocationList {\n @Field(type => [OperationalLocation])\n items: OperationalLocation[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * 「바로 아래에 몇이 있나」를 세는 곳.\n *\n * ── 물어본 요청만 센다 ──────────────────────────────────────────────────────\n * 처음에는 목록 리졸버가 늘 세게 했다. 그러면 **개수를 쓰지 않는 조회도 값을 치른다** — 고르기 팝업,\n * 자동완성, 다른 화면의 목록까지 전부. 로케이션 목록은 여러 곳에서 부른다.\n *\n * 그래서 `childCount` 를 필드 리졸버로 둔다. GraphQL 이 **물어본 요청에만** 이것을 부르므로, 안 묻는\n * 조회는 질의가 한 번뿐이다. 「이 목록은 개수가 필요하다/아니다」를 서버 쪽에 플래그로 두지 않아도\n * 된다 — 묻는 것이 곧 그 표시다.\n *\n * ── 물었으면 한 번에 센다 ───────────────────────────────────────────────────\n * 필드 리졸버는 줄마다 불린다. 거기서 그때그때 세면 `limit 20` 에 카운트 20회, 100 이면 100회다 —\n * N+1 이다.\n *\n * 그래서 목록 리졸버가 그 페이지를 요청에 남겨 두고(`locationPage`), 첫 줄이 물을 때 **페이지 전체를\n * 한 번에** 세어 요청 안에 담아 둔다. 담는 것은 **결과가 아니라 약속(Promise)** 이다 — 줄들이 거의\n * 동시에 묻기 때문에, 결과를 담으면 스무 개가 각자 질의를 띄운 뒤에야 채워진다.\n *\n * SELECT parent_id, COUNT(1) … WHERE domain = ? AND parent_id IN (…) GROUP BY parent_id\n *\n * `IN` 에 들어가는 것은 그 페이지의 아이디와 이름뿐이라 표 전체 크기와 무관하다.\n * `ix_ops_operational_location_1`(도메인 · 상위)이 이 질의를 받는다.\n *\n * ── 상위를 두 가지로 찾는다 ──────────────────────────────────────────────────\n * `parentId` 는 외래 키가 아니라 문자열이라 쓰는 쪽이 정한다. 지금 데이터는 아이디가 아니라 **이름**을\n * 담고 있다(`LINE-1` 의 상위가 `PLANT-1`). 한쪽만 보면 개수가 오류 보고 없이 0 이 된다.\n */\nasync function countChildren(locations: OperationalLocation[], domain: Domain): Promise<Map<string, number>> {\n const keys = [...new Set(locations.flatMap(location => [location.id, location.name]).filter(Boolean))]\n\n if (!keys.length) return new Map()\n\n const counted = await getRepository(OperationalLocation)\n .createQueryBuilder('location')\n .select('location.parentId', 'parentId')\n .addSelect('COUNT(1)', 'count')\n .where('location.domain = :domain', { domain: domain.id })\n .andWhere('location.parentId IN (:...keys)', { keys })\n .groupBy('location.parentId')\n .getRawMany()\n\n return new Map<string, number>(counted.map(row => [row.parentId, Number(row.count)]))\n}\n\n/**\n * 아이디로 적힌 자식과 이름으로 적힌 자식을 **더한다.**\n *\n * 같은 자식이 두 규약으로 중복 등록돼 있으면 개수가 부풀 수 있다. 그런 데이터는 이미 결함이지만, 여기서\n * 한쪽을 조용히 버리지는 않는다 — 버리면 어느 쪽이 빠졌는지 아무도 모른다.\n */\nfunction sumFor(location: OperationalLocation, counted: Map<string, number>): number {\n return (counted.get(location.id) || 0) + (location.name ? counted.get(location.name) || 0 : 0)\n}\n\n/**\n * **이 자리에 무엇이 있나** — 들어오는 관계.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 자리 하나를 열어도 「여기서 누가 일하나 · 무슨 설비가 있나」를 알 수 없었다. 사람 화면을 열어\n * 근무 자리로 걸러야 했고, 설비는 또 설비 화면에서 같은 일을 해야 했다. 다른 화면의 참조 칸을\n * 눌러 자리로 걸어 들어와도 그 자리가 무엇인지는 이름뿐이었다.\n *\n * ── 줄마다 세지 않는다 ─────────────────────────────────────────────────────\n * `childCount` 와 같은 방식이다. 목록 리졸버가 그 페이지를 요청에 남겨 두고, 첫 줄이 물을 때\n * **페이지 전체를 한 번에** 묻는다. 담는 것은 결과가 아니라 **약속**이다 — 줄들이 거의 동시에\n * 물으므로 결과를 담으면 스무 개가 각자 질의를 띄운다.\n *\n * ── 자리를 두 가지로 찾는다 ────────────────────────────────────────────────\n * `homeLocation` 은 외래 키가 아니라 문자열이라 쓰는 쪽이 정한다. 지금 자료는 **이름**을 담는다\n * (`P-ECU-S-0001` 의 근무 자리가 `ECU-SUB-01`). 아이디도 함께 넣어 묻는다 — 한쪽만 보면 목록이\n * 오류 보고 없이 빈다. `childCount` 가 같은 이유로 그렇게 한다.\n */\n\n/** 한 자리에 보일 이름 수. 넘치면 몇이 남았는지 함께 낸다. */\nconst OCCUPANT_SHOWN = 8\n\ninterface OccupantScan {\n staff: Map<string, string[]>\n equipment: Map<string, string[]>\n assets: Map<string, string[]>\n}\n\nasync function scanOccupants(locations: OperationalLocation[], domain: Domain): Promise<OccupantScan> {\n const keys = [...new Set(locations.flatMap(one => [one.id, one.name]).filter(Boolean))] as string[]\n\n if (keys.length === 0) {\n return { staff: new Map(), equipment: new Map(), assets: new Map() }\n }\n\n const of = async (Entity: any) => {\n const rows = await getRepository(Entity)\n .createQueryBuilder('row')\n .select(['row.id', 'row.name', 'row.homeLocation'])\n .where('row.domain_id = :domain', { domain: domain.id })\n .andWhere('row.home_location IN (:...keys)', { keys })\n .getMany()\n\n return indexReferrers((rows as any[]).map(one => ({ points: one.homeLocation, name: one.name })))\n }\n\n const [staff, equipment, assets] = await Promise.all([of(Person), of(Equipment), of(PhysicalAsset)])\n\n return { staff, equipment, assets }\n}\n\n/**\n * 이 줄의 답을 낸다.\n *\n * **페이지에 없으면 그 줄만 따로 묻는다.** 한 요청에 목록 조회가 둘 있으면 뒤엣것이 앞엣것의\n * 페이지를 덮어쓰고, 그때 남는 줄들이 「아무도 없다」로 나온다 — 자리가 비어 있는 것과 못 본 것이\n * 같아 보인다. 들어오는 관계는 **비어 있는 것이 기본**이라 이 거짓말이 특히 안 띈다.\n * 틀린 목록보다 질의 한 번이 낫다. `childCount` 가 같은 이유로 같은 자리를 둔다.\n */\nasync function occupantsOf(location: OperationalLocation, context: ResolverContext): Promise<OccupantScan> {\n const state = context.state as {\n domain: Domain\n locationPage?: OperationalLocation[]\n occupantScan?: Promise<OccupantScan>\n }\n\n const page = state.locationPage\n const inPage = !!page?.some(one => one.id === location.id)\n\n if (!inPage) {\n return scanOccupants([location], state.domain)\n }\n\n /* 요청 안에서 한 번만 훑는다. 약속을 담는다. */\n state.occupantScan = state.occupantScan || scanOccupants(page!, state.domain)\n\n return state.occupantScan\n}\n\n/**\n * 한 자리의 이름들을 낸다 — 아이디로도 이름으로도 걸린 것을 합친다.\n *\n * 자료가 어느 쪽으로 적혔는지 자리마다 다를 수 있어 둘 다 본다. 합칠 때 겹치는 것은 한 번만 센다.\n */\nfunction namesAt(index: Map<string, string[]>, location: OperationalLocation): string[] {\n const found = [...(index.get(location.id) ?? []), ...(index.get(location.name) ?? [])]\n\n return [...new Set(found)].sort()\n}\n\n@Resolver(OperationalLocation)\n@UseMiddleware(SpeakRefusals)\nexport class OperationalLocationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => OperationalLocationList, { description: 'To fetch operational locations' })\n async operationalLocations(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OperationalLocationList> {\n const { domain } = context.state\n const { items, total } = await listMaster('operationalLocation', params, domain)\n\n const locations = items as OperationalLocation[]\n\n /*\n * 여기서 개수를 세지 않는다. 이 페이지만 요청에 남겨 둔다 — `childCount` 를 **물어본 요청**이\n * 있으면 그 리졸버가 이것을 보고 한 번에 센다(위 머리말).\n */\n context.state.locationPage = locations\n\n return { items: locations, total }\n }\n\n /**\n * 이 자리 바로 아래에 몇이 있나.\n *\n * 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 아이콘만으로는 그것을\n * 말하지 못한다 — 눌러 보기 전에는 안에 무엇이 있는지, 있기는 한지 알 수 없다.\n *\n * 세는 방식과 값은 `countChildren` 머리말에 적었다.\n */\n /**\n * 여기서 일하는 사람들.\n *\n * **넘치면 몇이 남았는지 말한다**(`occupantOverflow`). 조용히 자르면 「셋이 있다」와 「서른이\n * 있는데 셋만 보인다」가 같아 보이고, 그 칸을 보고 사람이 판단을 내린다.\n *\n * 아무도 없으면 빈 목록이다 — 없는 것은 없다고 낸다.\n */\n @FieldResolver(type => [String], { description: 'People whose home location is here' })\n async staff(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).staff, location), OCCUPANT_SHOWN).shown\n }\n\n /** 여기 있는 설비들. */\n @FieldResolver(type => [String], { description: 'Equipment stationed here' })\n async equipmentHere(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).equipment, location), OCCUPANT_SHOWN).shown\n }\n\n /** 여기 있는 자산들 — 공구 · 지그처럼 옮겨 다니는 것. */\n @FieldResolver(type => [String], { description: 'Physical assets kept here' })\n async assetsHere(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<string[]> {\n return cappedList(namesAt((await occupantsOf(location, context)).assets, location), OCCUPANT_SHOWN).shown\n }\n\n /**\n * 위 세 칸에서 **안 보인 것이 몇인가.**\n *\n * 셋을 합쳐 하나로 낸다. 칸마다 따로 두면 화면이 숫자 셋을 더 들고 있어야 하는데, 사람이 알고\n * 싶은 것은 「이 자리에 더 있나」 하나다.\n */\n @FieldResolver(type => Int, { description: 'How many occupants are not shown in the lists above' })\n async occupantOverflow(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<number> {\n const scan = await occupantsOf(location, context)\n\n return (\n cappedList(namesAt(scan.staff, location), OCCUPANT_SHOWN).hidden +\n cappedList(namesAt(scan.equipment, location), OCCUPANT_SHOWN).hidden +\n cappedList(namesAt(scan.assets, location), OCCUPANT_SHOWN).hidden\n )\n }\n\n @FieldResolver(type => Int, { description: 'How many locations sit directly under this one' })\n async childCount(@Root() location: OperationalLocation, @Ctx() context: ResolverContext): Promise<number> {\n const { domain, locationPage } = context.state as { domain: Domain; locationPage?: OperationalLocation[] }\n\n const page = locationPage as OperationalLocation[] | undefined\n const inPage = !!page?.some(one => one.id === location.id)\n\n /*\n * 목록 조회가 남긴 페이지에 없으면 그 줄만 센다.\n *\n * 한 요청에 목록 조회가 둘 있으면 뒤엣것이 앞엣것의 페이지를 덮어쓴다. 그때 남는 줄들을 조용히 0 으로\n * 두지 않는다 — 틀린 개수보다 질의 한 번이 낫다.\n */\n if (!inPage) {\n const counted = await countChildren([location], domain)\n return sumFor(location, counted)\n }\n\n /*\n * 요청 안에서 한 번만 센다. **약속을 담는다** — 줄들이 거의 동시에 묻기 때문에 결과를 담으면\n * 스무 개가 각자 질의를 띄운다.\n */\n const state = context.state as { locationChildCounts?: Promise<Map<string, number>> }\n state.locationChildCounts = state.locationChildCounts || countChildren(page!, domain)\n\n return sumFor(location, await state.locationChildCounts)\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [OperationalLocation], { description: 'To create, change or retire operational locations' })\n async updateMultipleOperationalLocation(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext,\n /** **언제부터 그러한가** — 근무조 선언과 부모 관계를 고칠 때만 온다(§`applied-when`). */\n @Arg('applied', type => ScalarObject, { nullable: true }) applied?: AppliedWhen\n ): Promise<OperationalLocation[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('operationalLocation')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod,\n ...appliedOptions(applied)\n })\n\n return saved as OperationalLocation[]\n }\n}\n"]}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { EntityManager } from 'typeorm';
|
|
2
2
|
import { Domain } from '@things-factory/shell';
|
|
3
3
|
import { User } from '@things-factory/auth-base';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* 정책을 정할 수 있는 축들 — 마스터 축 열다섯에 `workShift` 를 더한다.
|
|
6
|
+
*
|
|
7
|
+
* `workShift` 는 별도 모듈이라 `MASTER_AXES` 에 없는데 정책은 받는다(§`policyAxis`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const POLICY_AXES: string[];
|
|
10
|
+
/** 도메인 × 축의 기준정보 변경 정책 — 정하는 것도 바꾸는 것도 사업장 소유자의 몫이다. */
|
|
5
11
|
export declare class MasterChangePolicy {
|
|
6
12
|
id: string;
|
|
7
13
|
domain: Domain;
|
|
@@ -9,15 +15,72 @@ export declare class MasterChangePolicy {
|
|
|
9
15
|
axis: string;
|
|
10
16
|
approvalRequired: boolean;
|
|
11
17
|
reason: string;
|
|
18
|
+
/**
|
|
19
|
+
* 이 줄이 물러난 시각 — 비어 있는 줄이 **지금 정책**이다.
|
|
20
|
+
*
|
|
21
|
+
* 차례를 `createdAt` 으로 세우지 않는다. sqlite 의 `datetime('now')` 는 초까지라, 한 사람이 같은
|
|
22
|
+
* 초에 두 번 바꾸면 어느 줄이 나중인지 저장소가 못 가린다. 물러난 줄에 표를 하면 그 물음이 사라진다.
|
|
23
|
+
*/
|
|
24
|
+
supersededAt: Date;
|
|
25
|
+
/**
|
|
26
|
+
* 누가 정했나 — **비어 있으면 아무도 안 정한 것**이고, 그 줄은 기본값으로 선 줄이다.
|
|
27
|
+
*
|
|
28
|
+
* 사람이 안 정한 줄에 사람을 적으면 그 사람이 정한 것이 된다. 사업장을 만든 사람도 이 축을 보고
|
|
29
|
+
* 고른 적이 없다.
|
|
30
|
+
*/
|
|
12
31
|
creator: User;
|
|
13
32
|
creatorId: string;
|
|
14
33
|
createdAt: Date;
|
|
15
34
|
}
|
|
35
|
+
/** 아무도 안 정한 축이 도는 방식 — 바꾸려면 여기 하나만 고치면 된다. */
|
|
36
|
+
export declare const DEFAULT_APPROVAL_REQUIRED = false;
|
|
37
|
+
/**
|
|
38
|
+
* 이 축이 지금 어떻게 도는가. **아무도 안 정했으면 기본값으로 한 줄을 세우고 그것을 답한다.**
|
|
39
|
+
*
|
|
40
|
+
* 전에는 거절했다(`MASTER_POLICY_REQUIRED`). 그래서 새 사업장은 축 열여섯이 전부 막힌 채로 시작했고,
|
|
41
|
+
* 기준정보를 만들려던 사람이 「정책을 먼저 정하라」는 말만 받았다 — 어디서 정하는지는 안 적혀 있었다.
|
|
42
|
+
*
|
|
43
|
+
* 값을 그때그때 지어내지 않고 **줄을 세우는** 까닭은, 개정본이 자기가 어느 정책 아래 만들어졌는지를
|
|
44
|
+
* 그 줄로 가리키기 때문이다(ADR-0090). 가리킬 줄이 없으면 그 물음에 나중에 답할 수 없다.
|
|
45
|
+
*
|
|
46
|
+
* 그 줄의 `creator` 는 비어 있다. 아무도 고른 적이 없다는 것이 사실이고, 사업장을 만든 사람을 적으면
|
|
47
|
+
* 그 사람이 정한 것이 된다.
|
|
48
|
+
*/
|
|
16
49
|
export declare function masterChangePolicy(manager: EntityManager, domainId: string, axis: string): Promise<MasterChangePolicy>;
|
|
50
|
+
/**
|
|
51
|
+
* 지금 서 있는 줄.
|
|
52
|
+
*
|
|
53
|
+
* 둘 이상이 서 있으면 **가장 늦게 만들어진 것**을 답한다. 그 상태는 이 코드가 못 만들지만, 물러남
|
|
54
|
+
* 표를 두기 전에 쌓인 줄들이 그렇게 남아 있다. 다음 선언이 전부 물러나게 하고 하나로 정리한다.
|
|
55
|
+
*/
|
|
56
|
+
export declare function standingPolicy(manager: EntityManager, domainId: string, axis: string): Promise<MasterChangePolicy>;
|
|
57
|
+
/** 이 도메인에서 지금 서 있는 줄 전부 — 축을 열쇠로. */
|
|
58
|
+
export declare function standingPolicies(manager: EntityManager, domainId: string): Promise<Map<string, MasterChangePolicy>>;
|
|
17
59
|
export declare function declareMasterChangePolicy(manager: EntityManager, domainId: string, actorId: string, axis: string, approvalRequired: boolean, reason: string): Promise<MasterChangePolicy>;
|
|
18
|
-
/** The common mutation boundary also protects not-yet-revisioned axes when declared regulated. */
|
|
19
60
|
export declare function assertDirectMasterChange(manager: EntityManager, domainId: string, axis: string): Promise<void>;
|
|
20
61
|
export declare class MasterChangePolicyResolver {
|
|
21
62
|
masterChangePolicy(axis: string, context: ResolverContext): Promise<MasterChangePolicy>;
|
|
63
|
+
/**
|
|
64
|
+
* 축 전부의 정책 상태 — **안 정한 것도 답한다.**
|
|
65
|
+
*
|
|
66
|
+
* `masterChangePolicy` 는 안 정했으면 거절한다. 그것은 「고치기 전에 정해라」를 말하는 자리라 맞지만,
|
|
67
|
+
* **정하러 들어가는 화면까지 막는다.** 그래서 목록은 따로 둔다 — 안 정한 축은 안 정했다고 답한다.
|
|
68
|
+
*
|
|
69
|
+
* `canDeclare` 는 **이 사람이 소유자인가**다. 화면이 스스로 판단하지 않게 서버가 말한다 —
|
|
70
|
+
* 눌러 보고 거절당하는 화면이 되지 않도록.
|
|
71
|
+
*/
|
|
72
|
+
masterChangePolicies(context: ResolverContext): Promise<{
|
|
73
|
+
canDeclare: boolean;
|
|
74
|
+
axes: {
|
|
75
|
+
axis: string;
|
|
76
|
+
declared: boolean;
|
|
77
|
+
byDefault: boolean;
|
|
78
|
+
approvalRequired: boolean;
|
|
79
|
+
reason: string;
|
|
80
|
+
declaredAt: Date;
|
|
81
|
+
approvalReady: boolean;
|
|
82
|
+
approvalActivity: string;
|
|
83
|
+
}[];
|
|
84
|
+
}>;
|
|
22
85
|
declareMasterChangePolicy(axis: string, approvalRequired: boolean, reason: string, context: ResolverContext): Promise<MasterChangePolicy>;
|
|
23
86
|
}
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MasterChangePolicyResolver = exports.MasterChangePolicy = void 0;
|
|
3
|
+
exports.MasterChangePolicyResolver = exports.DEFAULT_APPROVAL_REQUIRED = exports.MasterChangePolicy = exports.POLICY_AXES = void 0;
|
|
4
4
|
exports.masterChangePolicy = masterChangePolicy;
|
|
5
|
+
exports.standingPolicy = standingPolicy;
|
|
6
|
+
exports.standingPolicies = standingPolicies;
|
|
5
7
|
exports.declareMasterChangePolicy = declareMasterChangePolicy;
|
|
6
8
|
exports.assertDirectMasterChange = assertDirectMasterChange;
|
|
7
9
|
const tslib_1 = require("tslib");
|
|
8
10
|
const typeorm_1 = require("typeorm");
|
|
11
|
+
const typeorm_2 = require("typeorm");
|
|
9
12
|
const type_graphql_1 = require("type-graphql");
|
|
10
13
|
const shell_1 = require("@things-factory/shell");
|
|
11
14
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
12
15
|
const master_axis_1 = require("./master-axis");
|
|
16
|
+
const approval_route_1 = require("./approval-route");
|
|
13
17
|
// WorkShift owns a domain-wide arrangement, not a row in the generic master CRUD registry.
|
|
14
18
|
function policyAxis(axis) {
|
|
15
19
|
if (axis !== 'workShift')
|
|
16
20
|
(0, master_axis_1.masterAxisOf)(axis);
|
|
17
21
|
}
|
|
18
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* 정책을 정할 수 있는 축들 — 마스터 축 열다섯에 `workShift` 를 더한다.
|
|
24
|
+
*
|
|
25
|
+
* `workShift` 는 별도 모듈이라 `MASTER_AXES` 에 없는데 정책은 받는다(§`policyAxis`).
|
|
26
|
+
*/
|
|
27
|
+
exports.POLICY_AXES = [...master_axis_1.MASTER_AXES.map(one => one.key), 'workShift'];
|
|
28
|
+
/** 도메인 × 축의 기준정보 변경 정책 — 정하는 것도 바꾸는 것도 사업장 소유자의 몫이다. */
|
|
19
29
|
let MasterChangePolicy = class MasterChangePolicy {
|
|
20
30
|
};
|
|
21
31
|
exports.MasterChangePolicy = MasterChangePolicy;
|
|
@@ -44,7 +54,18 @@ tslib_1.__decorate([
|
|
|
44
54
|
tslib_1.__metadata("design:type", String)
|
|
45
55
|
], MasterChangePolicy.prototype, "reason", void 0);
|
|
46
56
|
tslib_1.__decorate([
|
|
47
|
-
(0, typeorm_1.
|
|
57
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
58
|
+
tslib_1.__metadata("design:type", Date
|
|
59
|
+
/**
|
|
60
|
+
* 누가 정했나 — **비어 있으면 아무도 안 정한 것**이고, 그 줄은 기본값으로 선 줄이다.
|
|
61
|
+
*
|
|
62
|
+
* 사람이 안 정한 줄에 사람을 적으면 그 사람이 정한 것이 된다. 사업장을 만든 사람도 이 축을 보고
|
|
63
|
+
* 고른 적이 없다.
|
|
64
|
+
*/
|
|
65
|
+
)
|
|
66
|
+
], MasterChangePolicy.prototype, "supersededAt", void 0);
|
|
67
|
+
tslib_1.__decorate([
|
|
68
|
+
(0, typeorm_1.ManyToOne)(() => auth_base_1.User, { nullable: true }),
|
|
48
69
|
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
49
70
|
], MasterChangePolicy.prototype, "creator", void 0);
|
|
50
71
|
tslib_1.__decorate([
|
|
@@ -56,16 +77,71 @@ tslib_1.__decorate([
|
|
|
56
77
|
tslib_1.__metadata("design:type", Date)
|
|
57
78
|
], MasterChangePolicy.prototype, "createdAt", void 0);
|
|
58
79
|
exports.MasterChangePolicy = MasterChangePolicy = tslib_1.__decorate([
|
|
59
|
-
(0, typeorm_1.Entity)('ops_master_change_policy')
|
|
60
|
-
|
|
80
|
+
(0, typeorm_1.Entity)('ops_master_change_policy')
|
|
81
|
+
/*
|
|
82
|
+
* 도메인 × 축에 **줄이 여럿 선다.** 정책은 바뀌고, 바뀌면 새 줄이 서고, 지난 개정본은 자기가 만들어질
|
|
83
|
+
* 때의 줄을 그대로 가리킨다 — 그래야 「그때는 무슨 규칙이었나」가 안 흔들린다.
|
|
84
|
+
*
|
|
85
|
+
* 전에는 유니크였다. 그래서 바꾸려면 그 줄을 덮어야 했고, 덮으면 승인받고 만든 개정본이 소급해서
|
|
86
|
+
* 승인 없이 만든 것이 됐다. 그래서 코드가 바꾸기를 거절했고, **정책을 영영 못 바꿨다.**
|
|
87
|
+
*
|
|
88
|
+
* ⚠ **이 정책을 바꾸는 일 자체는 결재에 안 태운다**(2026-09-22). 사업장 소유자가 정하는 것이라
|
|
89
|
+
* 소유자 확인만 받고 바로 선다. 태울 필요가 생기면 그때 결재 활동을 붙인다 — 줄이 여럿 서는
|
|
90
|
+
* 구조라 붙이는 자리는 이미 있다.
|
|
91
|
+
*/
|
|
92
|
+
,
|
|
93
|
+
(0, typeorm_1.Index)('ix_master_change_policy_axis', ['domain', 'axis']),
|
|
61
94
|
(0, typeorm_1.Index)('ix_master_change_policy_identity', ['domain', 'id'], { unique: true })
|
|
62
95
|
], MasterChangePolicy);
|
|
96
|
+
/** 아무도 안 정한 축이 도는 방식 — 바꾸려면 여기 하나만 고치면 된다. */
|
|
97
|
+
exports.DEFAULT_APPROVAL_REQUIRED = false;
|
|
98
|
+
/**
|
|
99
|
+
* 이 축이 지금 어떻게 도는가. **아무도 안 정했으면 기본값으로 한 줄을 세우고 그것을 답한다.**
|
|
100
|
+
*
|
|
101
|
+
* 전에는 거절했다(`MASTER_POLICY_REQUIRED`). 그래서 새 사업장은 축 열여섯이 전부 막힌 채로 시작했고,
|
|
102
|
+
* 기준정보를 만들려던 사람이 「정책을 먼저 정하라」는 말만 받았다 — 어디서 정하는지는 안 적혀 있었다.
|
|
103
|
+
*
|
|
104
|
+
* 값을 그때그때 지어내지 않고 **줄을 세우는** 까닭은, 개정본이 자기가 어느 정책 아래 만들어졌는지를
|
|
105
|
+
* 그 줄로 가리키기 때문이다(ADR-0090). 가리킬 줄이 없으면 그 물음에 나중에 답할 수 없다.
|
|
106
|
+
*
|
|
107
|
+
* 그 줄의 `creator` 는 비어 있다. 아무도 고른 적이 없다는 것이 사실이고, 사업장을 만든 사람을 적으면
|
|
108
|
+
* 그 사람이 정한 것이 된다.
|
|
109
|
+
*/
|
|
63
110
|
async function masterChangePolicy(manager, domainId, axis) {
|
|
64
111
|
policyAxis(axis);
|
|
65
|
-
const policy = await manager
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
112
|
+
const policy = await standingPolicy(manager, domainId, axis);
|
|
113
|
+
if (policy) {
|
|
114
|
+
return policy;
|
|
115
|
+
}
|
|
116
|
+
if (!manager.queryRunner?.isTransactionActive)
|
|
117
|
+
throw new shell_1.Refusal('TRANSACTION_REQUIRED', {}, 'A transaction is required.');
|
|
118
|
+
return manager.save(MasterChangePolicy, manager.create(MasterChangePolicy, {
|
|
119
|
+
domain: { id: domainId },
|
|
120
|
+
axis,
|
|
121
|
+
approvalRequired: exports.DEFAULT_APPROVAL_REQUIRED,
|
|
122
|
+
reason: 'nobody set this axis; it runs on the default'
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
/** 이 축에서 물러나지 않은 줄 전부 — 보통 하나다. */
|
|
126
|
+
function standingRows(manager, domainId, axis) {
|
|
127
|
+
return manager.findBy(MasterChangePolicy, { domain: { id: domainId }, axis, supersededAt: (0, typeorm_2.IsNull)() });
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 지금 서 있는 줄.
|
|
131
|
+
*
|
|
132
|
+
* 둘 이상이 서 있으면 **가장 늦게 만들어진 것**을 답한다. 그 상태는 이 코드가 못 만들지만, 물러남
|
|
133
|
+
* 표를 두기 전에 쌓인 줄들이 그렇게 남아 있다. 다음 선언이 전부 물러나게 하고 하나로 정리한다.
|
|
134
|
+
*/
|
|
135
|
+
async function standingPolicy(manager, domainId, axis) {
|
|
136
|
+
const rows = await standingRows(manager, domainId, axis);
|
|
137
|
+
return rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).pop() ?? null;
|
|
138
|
+
}
|
|
139
|
+
/** 이 도메인에서 지금 서 있는 줄 전부 — 축을 열쇠로. */
|
|
140
|
+
async function standingPolicies(manager, domainId) {
|
|
141
|
+
const rows = await manager.findBy(MasterChangePolicy, { domain: { id: domainId }, supersededAt: (0, typeorm_2.IsNull)() });
|
|
142
|
+
/* 이른 것부터 담아서 같은 축은 늦은 것이 이긴다 — `standingPolicy` 와 같은 답이 되도록. */
|
|
143
|
+
rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
144
|
+
return new Map(rows.map(one => [one.axis, one]));
|
|
69
145
|
}
|
|
70
146
|
async function declareMasterChangePolicy(manager, domainId, actorId, axis, approvalRequired, reason) {
|
|
71
147
|
if (!manager.queryRunner?.isTransactionActive)
|
|
@@ -73,32 +149,93 @@ async function declareMasterChangePolicy(manager, domainId, actorId, axis, appro
|
|
|
73
149
|
policyAxis(axis);
|
|
74
150
|
const domain = await manager.findOneByOrFail(shell_1.Domain, { id: domainId });
|
|
75
151
|
if (domain.owner !== actorId)
|
|
76
|
-
throw new shell_1.Refusal('MASTER_POLICY_OWNER_REQUIRED', {}, 'Only the domain owner may
|
|
152
|
+
throw new shell_1.Refusal('MASTER_POLICY_OWNER_REQUIRED', {}, 'Only the domain owner may declare or change the master change policy.');
|
|
77
153
|
if (!reason?.trim())
|
|
78
|
-
throw new shell_1.Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
154
|
+
throw new shell_1.Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the policy declaration.');
|
|
155
|
+
/*
|
|
156
|
+
* 규제를 **거는** 쪽만 본다. 푸는 쪽은 결재선이 비어 있어도 되고, 오히려 비어서 멈춘 축을 푸는 것이
|
|
157
|
+
* 이 화면으로 할 수 있는 일이어야 한다.
|
|
158
|
+
*/
|
|
159
|
+
if (approvalRequired) {
|
|
160
|
+
await (0, approval_route_1.assertApprovalRouteReady)(manager, domainId, axis);
|
|
161
|
+
}
|
|
162
|
+
const standing = await standingRows(manager, domainId, axis);
|
|
163
|
+
/*
|
|
164
|
+
* 같은 값을 다시 정하는 것은 아무 일도 아니다 — **하나만 서 있을 때** 그렇다. 여럿이 서 있으면
|
|
165
|
+
* 어느 것이 지금 것인지가 안 정해진 상태이므로, 값이 같아 보여도 전부 물리고 하나로 다시 세운다.
|
|
166
|
+
*/
|
|
167
|
+
if (standing.length === 1 && standing[0].approvalRequired === approvalRequired) {
|
|
168
|
+
return standing[0];
|
|
169
|
+
}
|
|
170
|
+
const supersededAt = new Date();
|
|
171
|
+
for (const row of standing) {
|
|
172
|
+
row.supersededAt = supersededAt;
|
|
173
|
+
await manager.save(MasterChangePolicy, row);
|
|
84
174
|
}
|
|
175
|
+
/*
|
|
176
|
+
* 바뀌면 **새 줄을 세운다. 있던 줄은 그대로 둔다.**
|
|
177
|
+
*
|
|
178
|
+
* 지난 개정본들이 그 줄을 가리키고 있다(`RecipeRevision.policy`, RESTRICT). 덮으면 그것들이
|
|
179
|
+
* 소급해서 다른 규칙 아래 만들어진 것이 된다 — 그래서 덮지 않고 새로 세운다. 오늘부터는 새 줄이
|
|
180
|
+
* 현재 정책이고, 지난 것은 자기 때의 줄을 계속 가리킨다.
|
|
181
|
+
*/
|
|
85
182
|
return manager.save(MasterChangePolicy, manager.create(MasterChangePolicy, {
|
|
86
|
-
domain,
|
|
183
|
+
domain: { id: domainId },
|
|
87
184
|
axis,
|
|
88
185
|
approvalRequired,
|
|
89
|
-
reason
|
|
186
|
+
reason,
|
|
90
187
|
creator: { id: actorId }
|
|
91
188
|
}));
|
|
92
189
|
}
|
|
93
|
-
/** The common mutation boundary also protects not-yet-revisioned axes when declared regulated. */
|
|
94
190
|
async function assertDirectMasterChange(manager, domainId, axis) {
|
|
95
|
-
const policy = await manager
|
|
191
|
+
const policy = await standingPolicy(manager, domainId, axis);
|
|
96
192
|
if (policy?.approvalRequired)
|
|
97
193
|
throw new shell_1.Refusal('MASTER_APPROVAL_REQUIRED', { axis }, 'This master axis requires approval in this domain.');
|
|
98
194
|
}
|
|
99
195
|
let MasterChangePolicyResolver = class MasterChangePolicyResolver {
|
|
100
196
|
async masterChangePolicy(axis, context) {
|
|
101
|
-
|
|
197
|
+
/* 읽기만 하는 자리다. 안 정한 축에 줄을 세우지 않는다 — 그것은 고치러 들어간 쪽이 할 일이다. */
|
|
198
|
+
return standingPolicy((0, shell_1.getRepository)(MasterChangePolicy).manager, context.state.domain.id, axis);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 축 전부의 정책 상태 — **안 정한 것도 답한다.**
|
|
202
|
+
*
|
|
203
|
+
* `masterChangePolicy` 는 안 정했으면 거절한다. 그것은 「고치기 전에 정해라」를 말하는 자리라 맞지만,
|
|
204
|
+
* **정하러 들어가는 화면까지 막는다.** 그래서 목록은 따로 둔다 — 안 정한 축은 안 정했다고 답한다.
|
|
205
|
+
*
|
|
206
|
+
* `canDeclare` 는 **이 사람이 소유자인가**다. 화면이 스스로 판단하지 않게 서버가 말한다 —
|
|
207
|
+
* 눌러 보고 거절당하는 화면이 되지 않도록.
|
|
208
|
+
*/
|
|
209
|
+
async masterChangePolicies(context) {
|
|
210
|
+
const { domain, user } = context.state;
|
|
211
|
+
const manager = (0, shell_1.getRepository)(MasterChangePolicy).manager;
|
|
212
|
+
/*
|
|
213
|
+
* **물러나지 않은 줄만** 읽는다. 축마다 줄이 여럿이라, 전부 담으면 저장소가 주는 차례대로 덮여서
|
|
214
|
+
* 바꾼 뒤에도 화면에 옛 값이 떴다(2026-09-22 화면에서 확인).
|
|
215
|
+
*/
|
|
216
|
+
const byAxis = await standingPolicies(manager, domain.id);
|
|
217
|
+
return {
|
|
218
|
+
canDeclare: domain.owner === user?.id,
|
|
219
|
+
axes: await Promise.all(exports.POLICY_AXES.map(async (axis) => {
|
|
220
|
+
const policy = byAxis.get(axis);
|
|
221
|
+
/*
|
|
222
|
+
* 결재 길이 준비됐나 — **화면이 누르기 전에 알아야 한다.** 답이 없는 축은 이 앱이 아직 이
|
|
223
|
+
* 물음에 답할 길이 없다는 뜻이라 막지 않는다(`inspectApprovalRoute`).
|
|
224
|
+
*/
|
|
225
|
+
const route = await (0, approval_route_1.inspectApprovalRoute)(manager, domain.id, axis);
|
|
226
|
+
return {
|
|
227
|
+
axis,
|
|
228
|
+
declared: !!policy,
|
|
229
|
+
/* 줄은 섰지만 사람이 고른 적은 없다 — 화면이 그것을 다르게 보인다. */
|
|
230
|
+
byDefault: !!policy && !policy.creatorId,
|
|
231
|
+
approvalRequired: policy?.approvalRequired ?? null,
|
|
232
|
+
reason: policy?.reason ?? null,
|
|
233
|
+
declaredAt: policy?.createdAt ?? null,
|
|
234
|
+
approvalReady: route ? route.ready : true,
|
|
235
|
+
approvalActivity: route?.activity ?? null
|
|
236
|
+
};
|
|
237
|
+
}))
|
|
238
|
+
};
|
|
102
239
|
}
|
|
103
240
|
async declareMasterChangePolicy(axis, approvalRequired, reason, context) {
|
|
104
241
|
const { tx, domain, user } = context.state;
|
|
@@ -115,6 +252,14 @@ tslib_1.__decorate([
|
|
|
115
252
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
116
253
|
tslib_1.__metadata("design:returntype", Promise)
|
|
117
254
|
], MasterChangePolicyResolver.prototype, "masterChangePolicy", null);
|
|
255
|
+
tslib_1.__decorate([
|
|
256
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
|
|
257
|
+
(0, type_graphql_1.Query)(() => shell_1.ScalarObject),
|
|
258
|
+
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
259
|
+
tslib_1.__metadata("design:type", Function),
|
|
260
|
+
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
261
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
262
|
+
], MasterChangePolicyResolver.prototype, "masterChangePolicies", null);
|
|
118
263
|
tslib_1.__decorate([
|
|
119
264
|
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
|
|
120
265
|
(0, type_graphql_1.Directive)('@transaction'),
|