@things-factory/worklist 10.1.6 → 10.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist-client/components/activity-instance-preview.js +5 -4
  2. package/dist-client/components/activity-instance-preview.js.map +1 -1
  3. package/dist-client/components/activity-starter-form.js +14 -10
  4. package/dist-client/components/activity-starter-form.js.map +1 -1
  5. package/dist-client/components/activity-thread-timeline.d.ts +2 -2
  6. package/dist-client/components/decision-slots.d.ts +38 -9
  7. package/dist-client/components/decision-slots.js +24 -10
  8. package/dist-client/components/decision-slots.js.map +1 -1
  9. package/dist-client/components/mutation-outcome.d.ts +57 -0
  10. package/dist-client/components/mutation-outcome.js +78 -0
  11. package/dist-client/components/mutation-outcome.js.map +1 -0
  12. package/dist-client/components/say-outcome.d.ts +25 -0
  13. package/dist-client/components/say-outcome.js +51 -0
  14. package/dist-client/components/say-outcome.js.map +1 -0
  15. package/dist-client/pages/activity/activity-list-page.js +28 -12
  16. package/dist-client/pages/activity/activity-list-page.js.map +1 -1
  17. package/dist-client/pages/activity/activity-model-item-list.js +4 -3
  18. package/dist-client/pages/activity/activity-model-item-list.js.map +1 -1
  19. package/dist-client/pages/activity/activity-page.d.ts +2 -2
  20. package/dist-client/pages/activity/activity-page.js +25 -21
  21. package/dist-client/pages/activity/activity-page.js.map +1 -1
  22. package/dist-client/pages/activity-approval/activity-approval-importer.js +4 -3
  23. package/dist-client/pages/activity-approval/activity-approval-importer.js.map +1 -1
  24. package/dist-client/pages/activity-approval/activity-approval-list-page.js +8 -9
  25. package/dist-client/pages/activity-approval/activity-approval-list-page.js.map +1 -1
  26. package/dist-client/pages/activity-approval/activity-approval-page.d.ts +3 -1
  27. package/dist-client/pages/activity-approval/activity-approval-page.js +76 -28
  28. package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
  29. package/dist-client/pages/activity-instance/activity-instance-start-page.js +24 -18
  30. package/dist-client/pages/activity-instance/activity-instance-start-page.js.map +1 -1
  31. package/dist-client/pages/activity-instance/activity-instance-view.js +1 -0
  32. package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
  33. package/dist-client/pages/activity-stats/activity-stats-importer.js +4 -3
  34. package/dist-client/pages/activity-stats/activity-stats-importer.js.map +1 -1
  35. package/dist-client/pages/activity-stats/activity-stats-list-page.js +8 -9
  36. package/dist-client/pages/activity-stats/activity-stats-list-page.js.map +1 -1
  37. package/dist-client/pages/activity-template/activity-template-importer.js +4 -3
  38. package/dist-client/pages/activity-template/activity-template-importer.js.map +1 -1
  39. package/dist-client/pages/activity-template/activity-template-list-page.js +8 -9
  40. package/dist-client/pages/activity-template/activity-template-list-page.js.map +1 -1
  41. package/dist-client/pages/activity-template/activity-template-model-item-list.js +4 -3
  42. package/dist-client/pages/activity-template/activity-template-model-item-list.js.map +1 -1
  43. package/dist-client/pages/activity-thread/activity-thread-importer.js +4 -3
  44. package/dist-client/pages/activity-thread/activity-thread-importer.js.map +1 -1
  45. package/dist-client/pages/activity-thread/activity-thread-list-page.js +8 -9
  46. package/dist-client/pages/activity-thread/activity-thread-list-page.js.map +1 -1
  47. package/dist-client/pages/activity-thread/activity-thread-page.d.ts +1 -1
  48. package/dist-client/pages/activity-thread/activity-thread-page.js +22 -20
  49. package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
  50. package/dist-client/pages/activity-thread/activity-thread-view-page.d.ts +1 -1
  51. package/dist-client/pages/activity-thread/activity-thread-view-page.js +1 -0
  52. package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
  53. package/dist-client/pages/activity-thread/activity-thread-view.d.ts +1 -1
  54. package/dist-client/pages/todo/draft-list-page.js +1 -0
  55. package/dist-client/pages/todo/draft-list-page.js.map +1 -1
  56. package/dist-client/route.d.ts +1 -1
  57. package/dist-client/tsconfig.tsbuildinfo +1 -1
  58. package/dist-server/controllers/activity-installation/installable-to-activity.d.ts +14 -1
  59. package/dist-server/controllers/activity-installation/installable-to-activity.js +14 -1
  60. package/dist-server/controllers/activity-installation/installable-to-activity.js.map +1 -1
  61. package/dist-server/controllers/activity-instance/draft.js +3 -0
  62. package/dist-server/controllers/activity-instance/draft.js.map +1 -1
  63. package/dist-server/controllers/activity-instance/entitlement.d.ts +25 -0
  64. package/dist-server/controllers/activity-instance/entitlement.js +108 -0
  65. package/dist-server/controllers/activity-instance/entitlement.js.map +1 -0
  66. package/dist-server/controllers/activity-instance/issue.js +10 -0
  67. package/dist-server/controllers/activity-instance/issue.js.map +1 -1
  68. package/dist-server/controllers/activity-instance/pick.js +8 -0
  69. package/dist-server/controllers/activity-instance/pick.js.map +1 -1
  70. package/dist-server/service/activity/index.d.ts +1 -1
  71. package/dist-server/service/activity-instance/activity-instance-mutation.js +20 -9
  72. package/dist-server/service/activity-instance/activity-instance-mutation.js.map +1 -1
  73. package/dist-server/service/activity-instance/activity-instance-query.js +25 -2
  74. package/dist-server/service/activity-instance/activity-instance-query.js.map +1 -1
  75. package/dist-server/service/activity-instance/index.d.ts +1 -1
  76. package/dist-server/service/activity-template/index.d.ts +1 -1
  77. package/dist-server/service/activity-thread/index.d.ts +1 -1
  78. package/dist-server/service/index.d.ts +3 -3
  79. package/dist-server/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +11 -11
  81. package/translations/en.json +5 -0
  82. package/translations/ja.json +5 -0
  83. package/translations/ko.json +5 -0
  84. package/translations/ms.json +5 -0
  85. package/translations/zh.json +5 -0
@@ -43,7 +43,20 @@ export declare const ACTIVITY_ROW_FIELDS: readonly ["name", "label", "descriptio
43
43
  * The part of a template that is stored on the Activity row.
44
44
  *
45
45
  * A field the template left undefined is not copied, so installing again does not blank a value
46
- * somebody edited on the row — `save` with an explicit undefined writes null.
46
+ * somebody edited on the row — `save` with an explicit undefined writes null. That protection is
47
+ * real: the activity list screen lets a domain edit viewType and viewSource on the row, and
48
+ * installActivity runs at every boot.
49
+ *
50
+ * ── So leaving a field out is not the same as clearing it ─────────────────
51
+ * A template that stops providing something has to say so, because silence means "keep whatever the
52
+ * row has":
53
+ *
54
+ * viewSource: undefined the template says nothing — the row keeps its value
55
+ * viewSource: '' the template says there is none — the row is cleared
56
+ *
57
+ * plant hit this: they removed two viewSource declarations, the rows kept pointing at elements that
58
+ * no longer existed, and approvers met a screen that could not load. Clearing is written as an empty
59
+ * string. (2026-09-11)
47
60
  */
48
61
  export declare function activityRowFrom(template: object | undefined): TemplateFields;
49
62
  export {};
@@ -24,7 +24,20 @@ exports.ACTIVITY_ROW_FIELDS = [
24
24
  * The part of a template that is stored on the Activity row.
25
25
  *
26
26
  * A field the template left undefined is not copied, so installing again does not blank a value
27
- * somebody edited on the row — `save` with an explicit undefined writes null.
27
+ * somebody edited on the row — `save` with an explicit undefined writes null. That protection is
28
+ * real: the activity list screen lets a domain edit viewType and viewSource on the row, and
29
+ * installActivity runs at every boot.
30
+ *
31
+ * ── So leaving a field out is not the same as clearing it ─────────────────
32
+ * A template that stops providing something has to say so, because silence means "keep whatever the
33
+ * row has":
34
+ *
35
+ * viewSource: undefined the template says nothing — the row keeps its value
36
+ * viewSource: '' the template says there is none — the row is cleared
37
+ *
38
+ * plant hit this: they removed two viewSource declarations, the rows kept pointing at elements that
39
+ * no longer existed, and approvers met a screen that could not load. Clearing is written as an empty
40
+ * string. (2026-09-11)
28
41
  */
29
42
  function activityRowFrom(template) {
30
43
  const fields = (template ?? {});
@@ -1 +1 @@
1
- {"version":3,"file":"installable-to-activity.js","sourceRoot":"","sources":["../../../server/controllers/activity-installation/installable-to-activity.ts"],"names":[],"mappings":";;;AA+DA,0CAaC;AAtCY,QAAA,mBAAmB,GAAG;IACjC,MAAM;IACN,OAAO;IACP,aAAa;IACb,cAAc;IACd,UAAU;IACV,cAAc;IACd,UAAU;IACV,OAAO;IACP,cAAc;IACd,QAAQ;IACR,UAAU;IACV,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,WAAW;CACH,CAAA;AAEV;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,QAA4B;IAC1D,MAAM,MAAM,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAmB,CAAA;IACjD,MAAM,GAAG,GAAmB,EAAE,CAAA;IAE9B,KAAK,MAAM,KAAK,IAAI,2BAAmB,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAE3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAA;QACpB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["/**\n * The template fields that belong on the Activity row, named one by one.\n *\n * ── Why not spread the template ───────────────────────────────────────────\n * installActivity used `{ ...(template as any) }`. A template field with no column on Activity was\n * dropped by the save, which reported success — and `as any` meant the types said nothing about it.\n * Nine fields were being handed over with no home: activity, approvalSteps, callback, category,\n * guide, label, provider, readiness, release.\n *\n * `label` was the one that hurt. It is the display name a person reads, and it reached exactly one\n * screen — the catalog, which reads the template registry directly — while every screen reading the\n * row showed the instance name instead. decisionKeys only worked because a column happened to be\n * added for it in the same change; the next field would have been left to the same luck.\n *\n * Naming the fields buys three things: a new template field has to be wired here on purpose, the\n * eight that stay behind are recorded as catalog-only rather than lost, and the two `as any` casts\n * go away. (2026-09-10, architect's ruling)\n *\n * ── What stays in the template registry, and why ──────────────────────────\n * activity the installed row itself, attached by the catalog query. Writing it back would\n * be a row pointing at itself\n * approvalSteps counted per domain by the catalog query from the line that is actually set up\n * callback a function\n * category how the catalog groups it\n * guide what the work is, for the catalog's reader\n * provider which module registered it\n * readiness how far along the registering module says it is\n * release which version registered it\n *\n * Each of those is answered by the registry, which is loaded in every process that serves the\n * catalog. Copying them to the row would make a second copy that goes stale on the next release.\n *\n * ── The template shape is spelled out, not imported ───────────────────────\n * InstallableActivity carries type-graphql decorators, and the test runner strips types without\n * reading them, so importing it takes the whole test file down. That happened three times in one\n * day — to search-key-declaration, to decision-key-declaration, and here.\n */\ntype TemplateFields = { [field: string]: unknown }\nexport const ACTIVITY_ROW_FIELDS = [\n 'name',\n 'label',\n 'description',\n 'activityType',\n 'priority',\n 'startingType',\n 'multiple',\n 'model',\n 'decisionKeys',\n 'uiType',\n 'uiSource',\n 'viewType',\n 'viewSource',\n 'reportType',\n 'reportSource',\n 'thumbnail'\n] as const\n\n/**\n * The part of a template that is stored on the Activity row.\n *\n * A field the template left undefined is not copied, so installing again does not blank a value\n * somebody edited on the row — `save` with an explicit undefined writes null.\n */\nexport function activityRowFrom(template: object | undefined): TemplateFields {\n const fields = (template ?? {}) as TemplateFields\n const row: TemplateFields = {}\n\n for (const field of ACTIVITY_ROW_FIELDS) {\n const value = fields[field]\n\n if (value !== undefined) {\n row[field] = value\n }\n }\n\n return row\n}\n"]}
1
+ {"version":3,"file":"installable-to-activity.js","sourceRoot":"","sources":["../../../server/controllers/activity-installation/installable-to-activity.ts"],"names":[],"mappings":";;;AA4EA,0CAaC;AAnDY,QAAA,mBAAmB,GAAG;IACjC,MAAM;IACN,OAAO;IACP,aAAa;IACb,cAAc;IACd,UAAU;IACV,cAAc;IACd,UAAU;IACV,OAAO;IACP,cAAc;IACd,QAAQ;IACR,UAAU;IACV,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,WAAW;CACH,CAAA;AAEV;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,eAAe,CAAC,QAA4B;IAC1D,MAAM,MAAM,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAmB,CAAA;IACjD,MAAM,GAAG,GAAmB,EAAE,CAAA;IAE9B,KAAK,MAAM,KAAK,IAAI,2BAAmB,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAE3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAA;QACpB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["/**\n * The template fields that belong on the Activity row, named one by one.\n *\n * ── Why not spread the template ───────────────────────────────────────────\n * installActivity used `{ ...(template as any) }`. A template field with no column on Activity was\n * dropped by the save, which reported success — and `as any` meant the types said nothing about it.\n * Nine fields were being handed over with no home: activity, approvalSteps, callback, category,\n * guide, label, provider, readiness, release.\n *\n * `label` was the one that hurt. It is the display name a person reads, and it reached exactly one\n * screen — the catalog, which reads the template registry directly — while every screen reading the\n * row showed the instance name instead. decisionKeys only worked because a column happened to be\n * added for it in the same change; the next field would have been left to the same luck.\n *\n * Naming the fields buys three things: a new template field has to be wired here on purpose, the\n * eight that stay behind are recorded as catalog-only rather than lost, and the two `as any` casts\n * go away. (2026-09-10, architect's ruling)\n *\n * ── What stays in the template registry, and why ──────────────────────────\n * activity the installed row itself, attached by the catalog query. Writing it back would\n * be a row pointing at itself\n * approvalSteps counted per domain by the catalog query from the line that is actually set up\n * callback a function\n * category how the catalog groups it\n * guide what the work is, for the catalog's reader\n * provider which module registered it\n * readiness how far along the registering module says it is\n * release which version registered it\n *\n * Each of those is answered by the registry, which is loaded in every process that serves the\n * catalog. Copying them to the row would make a second copy that goes stale on the next release.\n *\n * ── The template shape is spelled out, not imported ───────────────────────\n * InstallableActivity carries type-graphql decorators, and the test runner strips types without\n * reading them, so importing it takes the whole test file down. That happened three times in one\n * day — to search-key-declaration, to decision-key-declaration, and here.\n */\ntype TemplateFields = { [field: string]: unknown }\nexport const ACTIVITY_ROW_FIELDS = [\n 'name',\n 'label',\n 'description',\n 'activityType',\n 'priority',\n 'startingType',\n 'multiple',\n 'model',\n 'decisionKeys',\n 'uiType',\n 'uiSource',\n 'viewType',\n 'viewSource',\n 'reportType',\n 'reportSource',\n 'thumbnail'\n] as const\n\n/**\n * The part of a template that is stored on the Activity row.\n *\n * A field the template left undefined is not copied, so installing again does not blank a value\n * somebody edited on the row — `save` with an explicit undefined writes null. That protection is\n * real: the activity list screen lets a domain edit viewType and viewSource on the row, and\n * installActivity runs at every boot.\n *\n * ── So leaving a field out is not the same as clearing it ─────────────────\n * A template that stops providing something has to say so, because silence means \"keep whatever the\n * row has\":\n *\n * viewSource: undefined the template says nothing — the row keeps its value\n * viewSource: '' the template says there is none — the row is cleared\n *\n * plant hit this: they removed two viewSource declarations, the rows kept pointing at elements that\n * no longer existed, and approvers met a screen that could not load. Clearing is written as an empty\n * string. (2026-09-11)\n */\nexport function activityRowFrom(template: object | undefined): TemplateFields {\n const fields = (template ?? {}) as TemplateFields\n const row: TemplateFields = {}\n\n for (const field of ACTIVITY_ROW_FIELDS) {\n const value = fields[field]\n\n if (value !== undefined) {\n row[field] = value\n }\n }\n\n return row\n}\n"]}
@@ -4,6 +4,7 @@ exports.draft = draft;
4
4
  const activity_instance_js_1 = require("../../service/activity-instance/activity-instance.js");
5
5
  const activity_js_1 = require("../../service/activity/activity.js");
6
6
  const common_js_1 = require("../common.js");
7
+ const entitlement_js_1 = require("./entitlement.js");
7
8
  async function draft(activityInstance, context) {
8
9
  const { domain, user, tx } = context.state;
9
10
  const { id, activityId, input, dueAt } = activityInstance;
@@ -19,6 +20,8 @@ async function draft(activityInstance, context) {
19
20
  activityInstance: id
20
21
  }));
21
22
  }
23
+ /* 남의 초안은 못 고친다. 새로 시작하는 것(id 없음)은 이 검사에 안 걸린다. */
24
+ await (0, entitlement_js_1.assertMayWorkOnDraft)(previousActivityInstance, context);
22
25
  if (previousActivityInstance.state !== activity_instance_js_1.ActivityInstanceStatus.Draft) {
23
26
  throw new Error(context.t('error.activityInstance state should be draft', {
24
27
  activityInstance: id,
@@ -1 +1 @@
1
- {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityInstanceDraft } from '../../service/activity-instance/activity-instance-type.js'\nimport { Activity } from '../../service/activity/activity.js'\nimport { fillActivitySearchKeys } from '../common.js'\n\nexport async function draft(\n activityInstance: ActivityInstanceDraft,\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const { id, activityId, input, dueAt } = activityInstance\n\n var repository = tx.getRepository(Activity)\n var activity = {} as Activity\n\n if (id) {\n var previousActivityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['activity']\n })\n\n if (!previousActivityInstance) {\n throw new Error(\n context.t('error.activityInstance not found', {\n activityInstance: id\n })\n )\n }\n\n if (previousActivityInstance.state !== ActivityInstanceStatus.Draft) {\n throw new Error(\n context.t('error.activityInstance state should be draft', {\n activityInstance: id,\n state: previousActivityInstance.state\n })\n )\n }\n\n activity = previousActivityInstance.activity\n } else {\n activityInstance.adhocType = 'standard'\n activityInstance.refBy = activityId\n }\n\n if (activityId) {\n activity = await repository.findOne({\n where: { domain: { id: domain.id }, id: activityId },\n relations: ['assigneeRole', 'supervisoryRole']\n })\n\n if (!activity) {\n throw new Error(\n context.t('error.activity not found', {\n activity: activityId\n })\n )\n }\n }\n\n if (!activityInstance.assignees) {\n activityInstance.assignees = activity.assignees\n }\n if (!activityInstance.approvalLine) {\n activityInstance.approvalLine = activity.approvalLine\n }\n\n if (!dueAt && activity.standardTime) {\n activityInstance.dueAt = new Date(Date.now() + activity.standardTime * 1000)\n }\n\n const activitySearchKeys = fillActivitySearchKeys(activity?.searchKeys, input)\n\n return await tx.getRepository(ActivityInstance).save({\n ...previousActivityInstance,\n activityType: activity.activityType,\n uiType: activity.uiType,\n uiSource: activity.uiSource,\n viewType: activity.viewType,\n viewSource: activity.viewSource,\n assigneeRole: activity.assigneeRole,\n supervisoryRole: activity.supervisoryRole,\n ...activityInstance,\n transaction: 'draft',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Draft,\n domain,\n creator: user,\n updater: user\n })\n}\n"]}
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityInstanceDraft } from '../../service/activity-instance/activity-instance-type.js'\nimport { Activity } from '../../service/activity/activity.js'\nimport { fillActivitySearchKeys } from '../common.js'\nimport { assertMayWorkOnDraft } from './entitlement.js'\n\nexport async function draft(\n activityInstance: ActivityInstanceDraft,\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const { id, activityId, input, dueAt } = activityInstance\n\n var repository = tx.getRepository(Activity)\n var activity = {} as Activity\n\n if (id) {\n var previousActivityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['activity']\n })\n\n if (!previousActivityInstance) {\n throw new Error(\n context.t('error.activityInstance not found', {\n activityInstance: id\n })\n )\n }\n\n /* 남의 초안은 못 고친다. 새로 시작하는 것(id 없음)은 이 검사에 안 걸린다. */\n await assertMayWorkOnDraft(previousActivityInstance, context)\n\n if (previousActivityInstance.state !== ActivityInstanceStatus.Draft) {\n throw new Error(\n context.t('error.activityInstance state should be draft', {\n activityInstance: id,\n state: previousActivityInstance.state\n })\n )\n }\n\n activity = previousActivityInstance.activity\n } else {\n activityInstance.adhocType = 'standard'\n activityInstance.refBy = activityId\n }\n\n if (activityId) {\n activity = await repository.findOne({\n where: { domain: { id: domain.id }, id: activityId },\n relations: ['assigneeRole', 'supervisoryRole']\n })\n\n if (!activity) {\n throw new Error(\n context.t('error.activity not found', {\n activity: activityId\n })\n )\n }\n }\n\n if (!activityInstance.assignees) {\n activityInstance.assignees = activity.assignees\n }\n if (!activityInstance.approvalLine) {\n activityInstance.approvalLine = activity.approvalLine\n }\n\n if (!dueAt && activity.standardTime) {\n activityInstance.dueAt = new Date(Date.now() + activity.standardTime * 1000)\n }\n\n const activitySearchKeys = fillActivitySearchKeys(activity?.searchKeys, input)\n\n return await tx.getRepository(ActivityInstance).save({\n ...previousActivityInstance,\n activityType: activity.activityType,\n uiType: activity.uiType,\n uiSource: activity.uiSource,\n viewType: activity.viewType,\n viewSource: activity.viewSource,\n assigneeRole: activity.assigneeRole,\n supervisoryRole: activity.supervisoryRole,\n ...activityInstance,\n transaction: 'draft',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Draft,\n domain,\n creator: user,\n updater: user\n })\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { ActivityInstance } from '../../service/activity-instance/activity-instance.js';
2
+ /**
3
+ * May this person edit or issue this draft?
4
+ *
5
+ * The person who wrote it, or someone who manages activity instances. A draft is a private
6
+ * thing until it is issued — it carries an approval line and assignees that become other
7
+ * people's obligations the moment it goes out.
8
+ */
9
+ export declare function assertMayWorkOnDraft(activityInstance: ActivityInstance, context: ResolverContext): Promise<void>;
10
+ /**
11
+ * May this person pick up this task?
12
+ *
13
+ * ── When a role is named, it is the answer ───────────────────────────────────
14
+ * `assigneeRole` is how an issuer says "this is for the quality inspectors". Reading it is
15
+ * the whole point of writing it, and until now it was loaded and ignored.
16
+ *
17
+ * ── When no role is named, anyone in the domain may take it ──────────────────
18
+ * That is the current behaviour and it is deliberate rather than a fallback: an issuer who
19
+ * names nobody is saying "whoever is free". Refusing instead would make every unaddressed
20
+ * task unpickable, which is the opposite of what leaving the field blank means.
21
+ *
22
+ * `reference_absent_value_leans_which_way` asks which way an absent value should lean, and
23
+ * here the lean is the issuer's stated intent — not a guess the code makes to be safe.
24
+ */
25
+ export declare function assertMayPick(activityInstance: ActivityInstance, context: ResolverContext): Promise<void>;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertMayWorkOnDraft = assertMayWorkOnDraft;
4
+ exports.assertMayPick = assertMayPick;
5
+ const auth_base_1 = require("@things-factory/auth-base");
6
+ /**
7
+ * Who may act on **this one** activity instance.
8
+ *
9
+ * ── Why three mutations carry no `@privilege` ────────────────────────────────
10
+ * `draftActivityInstance`, `issueActivityInstance` and `pickActivityInstance` were given
11
+ * `@privilege(category: "activity-instance", privilege: "mutation", domainOwnerGranted: true)`
12
+ * commented out, in the same commit that put it live on the other six
13
+ * (`74acc61f6d`, 2025-04-10). That was a choice, not an omission, and the choice was right:
14
+ *
15
+ * gated acting on someone else's work, or destructive
16
+ * assign · end · abort · delete draft
17
+ * not gated doing your own work
18
+ * draft · issue · pick
19
+ *
20
+ * That privilege is the **management** one. Requiring it to pick up a task addressed to you
21
+ * would mean handing every operator on the floor the right to reassign and abort everyone
22
+ * else's work. So the category gate stays off here.
23
+ *
24
+ * The reason was never written down anywhere, which is why the next person to read the file
25
+ * saw three commented-out lines and could not tell "deliberate" from "forgotten". It is
26
+ * written here now.
27
+ *
28
+ * ── What was actually missing ────────────────────────────────────────────────
29
+ * "Your own work" was the intent and **nothing enforced it.** Each of the three takes an
30
+ * id and loads the record without asking whether this person has anything to do with it:
31
+ *
32
+ * draft(id) edits a draft — never checked who wrote it
33
+ * issue(id) issues a draft, routing approvals — never checked who wrote it
34
+ * pick(id) takes an unassigned task — loaded `assigneeRole` and never read it
35
+ *
36
+ * The category gate cannot fix that. Turning it on locks out the operator whose task it is;
37
+ * leaving it off lets anyone in the domain take, edit or issue anyone else's. Those are two
38
+ * different questions and only the second one is open:
39
+ *
40
+ * category privilege may this person manage activity instances at all
41
+ * entitlement may this person act on **this** record
42
+ *
43
+ * The approval side already has the second one — `reject.ts` refuses a reject from someone
44
+ * who is only a reference on the order. The assignment side had no equivalent.
45
+ *
46
+ * Starting something new is untouched: with no id there is no owner to check, and anyone in
47
+ * the domain may begin a task.
48
+ */
49
+ /** Whoever holds the management privilege is entitled to every record, as before. */
50
+ async function manages(context) {
51
+ const { domain, user, unsafeIP, prohibitedPrivileges } = context.state;
52
+ return await (0, auth_base_1.checkPermission)({ category: 'activity-instance', privilege: 'mutation', owner: true }, user, domain, unsafeIP, prohibitedPrivileges);
53
+ }
54
+ /**
55
+ * May this person edit or issue this draft?
56
+ *
57
+ * The person who wrote it, or someone who manages activity instances. A draft is a private
58
+ * thing until it is issued — it carries an approval line and assignees that become other
59
+ * people's obligations the moment it goes out.
60
+ */
61
+ async function assertMayWorkOnDraft(activityInstance, context) {
62
+ const { user } = context.state;
63
+ if (activityInstance.creatorId === user.id) {
64
+ return;
65
+ }
66
+ if (await manages(context)) {
67
+ return;
68
+ }
69
+ throw new Error(context.t('error.activity-instance belongs to someone else', {
70
+ id: activityInstance.id
71
+ }));
72
+ }
73
+ /**
74
+ * May this person pick up this task?
75
+ *
76
+ * ── When a role is named, it is the answer ───────────────────────────────────
77
+ * `assigneeRole` is how an issuer says "this is for the quality inspectors". Reading it is
78
+ * the whole point of writing it, and until now it was loaded and ignored.
79
+ *
80
+ * ── When no role is named, anyone in the domain may take it ──────────────────
81
+ * That is the current behaviour and it is deliberate rather than a fallback: an issuer who
82
+ * names nobody is saying "whoever is free". Refusing instead would make every unaddressed
83
+ * task unpickable, which is the opposite of what leaving the field blank means.
84
+ *
85
+ * `reference_absent_value_leans_which_way` asks which way an absent value should lean, and
86
+ * here the lean is the issuer's stated intent — not a guess the code makes to be safe.
87
+ */
88
+ async function assertMayPick(activityInstance, context) {
89
+ const { user, tx } = context.state;
90
+ const roleId = activityInstance.assigneeRoleId;
91
+ if (!roleId) {
92
+ return;
93
+ }
94
+ const holder = await tx.getRepository(auth_base_1.User).findOne({
95
+ where: { id: user.id },
96
+ relations: ['roles']
97
+ });
98
+ if ((holder?.roles || []).some((role) => role.id === roleId)) {
99
+ return;
100
+ }
101
+ if (await manages(context)) {
102
+ return;
103
+ }
104
+ throw new Error(context.t('error.activity-instance is addressed to another role', {
105
+ id: activityInstance.id
106
+ }));
107
+ }
108
+ //# sourceMappingURL=entitlement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/entitlement.ts"],"names":[],"mappings":";;AAoEA,oDAgBC;AAiBD,sCA0BC;AA/HD,yDAAuE;AAIvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,qFAAqF;AACrF,KAAK,UAAU,OAAO,CAAC,OAAwB;IAC7C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAEtE,OAAO,MAAM,IAAA,2BAAe,EAC1B,EAAE,QAAQ,EAAE,mBAAmB,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,EACrE,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,oBAAoB,CACrB,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,oBAAoB,CAAC,gBAAkC,EAAE,OAAwB;IACrG,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE9B,IAAI,gBAAgB,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3C,OAAM;IACR,CAAC;IAED,IAAI,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAM;IACR,CAAC;IAED,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,iDAAiD,EAAE;QAC3D,EAAE,EAAE,gBAAgB,CAAC,EAAE;KACxB,CAAC,CACH,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,aAAa,CAAC,gBAAkC,EAAE,OAAwB;IAC9F,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,cAAc,CAAA;IAE9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,gBAAI,CAAC,CAAC,OAAO,CAAC;QAClD,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACtB,SAAS,EAAE,CAAC,OAAO,CAAC;KACrB,CAAC,CAAA;IAEF,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;QACnE,OAAM;IACR,CAAC;IAED,IAAI,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAM;IACR,CAAC;IAED,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,sDAAsD,EAAE;QAChE,EAAE,EAAE,gBAAgB,CAAC,EAAE;KACxB,CAAC,CACH,CAAA;AACH,CAAC","sourcesContent":["import { checkPermission, Role, User } from '@things-factory/auth-base'\n\nimport { ActivityInstance } from '../../service/activity-instance/activity-instance.js'\n\n/**\n * Who may act on **this one** activity instance.\n *\n * ── Why three mutations carry no `@privilege` ────────────────────────────────\n * `draftActivityInstance`, `issueActivityInstance` and `pickActivityInstance` were given\n * `@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)`\n * commented out, in the same commit that put it live on the other six\n * (`74acc61f6d`, 2025-04-10). That was a choice, not an omission, and the choice was right:\n *\n * gated acting on someone else's work, or destructive\n * assign · end · abort · delete draft\n * not gated doing your own work\n * draft · issue · pick\n *\n * That privilege is the **management** one. Requiring it to pick up a task addressed to you\n * would mean handing every operator on the floor the right to reassign and abort everyone\n * else's work. So the category gate stays off here.\n *\n * The reason was never written down anywhere, which is why the next person to read the file\n * saw three commented-out lines and could not tell \"deliberate\" from \"forgotten\". It is\n * written here now.\n *\n * ── What was actually missing ────────────────────────────────────────────────\n * \"Your own work\" was the intent and **nothing enforced it.** Each of the three takes an\n * id and loads the record without asking whether this person has anything to do with it:\n *\n * draft(id) edits a draft — never checked who wrote it\n * issue(id) issues a draft, routing approvals — never checked who wrote it\n * pick(id) takes an unassigned task — loaded `assigneeRole` and never read it\n *\n * The category gate cannot fix that. Turning it on locks out the operator whose task it is;\n * leaving it off lets anyone in the domain take, edit or issue anyone else's. Those are two\n * different questions and only the second one is open:\n *\n * category privilege may this person manage activity instances at all\n * entitlement may this person act on **this** record\n *\n * The approval side already has the second one — `reject.ts` refuses a reject from someone\n * who is only a reference on the order. The assignment side had no equivalent.\n *\n * Starting something new is untouched: with no id there is no owner to check, and anyone in\n * the domain may begin a task.\n */\n\n/** Whoever holds the management privilege is entitled to every record, as before. */\nasync function manages(context: ResolverContext): Promise<boolean> {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n\n return await checkPermission(\n { category: 'activity-instance', privilege: 'mutation', owner: true },\n user,\n domain,\n unsafeIP,\n prohibitedPrivileges\n )\n}\n\n/**\n * May this person edit or issue this draft?\n *\n * The person who wrote it, or someone who manages activity instances. A draft is a private\n * thing until it is issued — it carries an approval line and assignees that become other\n * people's obligations the moment it goes out.\n */\nexport async function assertMayWorkOnDraft(activityInstance: ActivityInstance, context: ResolverContext) {\n const { user } = context.state\n\n if (activityInstance.creatorId === user.id) {\n return\n }\n\n if (await manages(context)) {\n return\n }\n\n throw new Error(\n context.t('error.activity-instance belongs to someone else', {\n id: activityInstance.id\n })\n )\n}\n\n/**\n * May this person pick up this task?\n *\n * ── When a role is named, it is the answer ───────────────────────────────────\n * `assigneeRole` is how an issuer says \"this is for the quality inspectors\". Reading it is\n * the whole point of writing it, and until now it was loaded and ignored.\n *\n * ── When no role is named, anyone in the domain may take it ──────────────────\n * That is the current behaviour and it is deliberate rather than a fallback: an issuer who\n * names nobody is saying \"whoever is free\". Refusing instead would make every unaddressed\n * task unpickable, which is the opposite of what leaving the field blank means.\n *\n * `reference_absent_value_leans_which_way` asks which way an absent value should lean, and\n * here the lean is the issuer's stated intent — not a guess the code makes to be safe.\n */\nexport async function assertMayPick(activityInstance: ActivityInstance, context: ResolverContext) {\n const { user, tx } = context.state\n const roleId = activityInstance.assigneeRoleId\n\n if (!roleId) {\n return\n }\n\n const holder = await tx.getRepository(User).findOne({\n where: { id: user.id },\n relations: ['roles']\n })\n\n if ((holder?.roles || []).some((role: Role) => role.id === roleId)) {\n return\n }\n\n if (await manages(context)) {\n return\n }\n\n throw new Error(\n context.t('error.activity-instance is addressed to another role', {\n id: activityInstance.id\n })\n )\n}\n"]}
@@ -6,6 +6,7 @@ const activity_js_1 = require("../../service/activity/activity.js");
6
6
  const activity_instance_js_1 = require("../../service/activity-instance/activity-instance.js");
7
7
  const common_js_1 = require("../common.js");
8
8
  const submit_js_1 = require("../activity-thread/submit.js");
9
+ const entitlement_js_1 = require("./entitlement.js");
9
10
  const approval_expectation_js_1 = require("./approval-expectation.js");
10
11
  async function issue(activityInstance, context) {
11
12
  const { domain, user, tx } = context.state;
@@ -26,6 +27,15 @@ async function issue(activityInstance, context) {
26
27
  ]
27
28
  })
28
29
  : null;
30
+ /*
31
+ 남의 초안은 못 내보낸다.
32
+
33
+ 내보내는 순간 결재선과 담당자가 서고, 그것은 **다른 사람들의 의무**가 된다. 새로 시작하는
34
+ 것(id 없음)은 주인이 없으므로 이 검사에 안 걸린다. 자격 규칙은 `entitlement.ts` 에 있다.
35
+ */
36
+ if (origin) {
37
+ await (0, entitlement_js_1.assertMayWorkOnDraft)(origin, context);
38
+ }
29
39
  /*
30
40
  Prerequisites for a Task to Be Issued.
31
41
  - The previous state of the task should be Draft.
@@ -1 +1 @@
1
- {"version":3,"file":"issue.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/issue.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Activity } from '../../service/activity/activity.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityInstanceIssue } from '../../service/activity-instance/activity-instance-type.js'\nimport {\n createActivityThreadWithUnassigned,\n createActivityThreadsForAllRoleUsers,\n createActivityThreadsForUsers,\n getSystemUserFromOrgMemberItem,\n createActivityThreadForUser,\n fillActivitySearchKeys\n} from '../common.js'\nimport { submit } from '../activity-thread/submit.js'\nimport { expectationBroken, judgeApprovalExpectation } from './approval-expectation.js'\n\nexport async function issue(\n activityInstance: ActivityInstanceIssue,\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const issuedAt = new Date()\n var {\n id,\n name,\n description,\n assignees,\n uiType,\n uiSource,\n viewType,\n viewSource,\n dueAt,\n priority,\n approvalLine,\n activityId,\n activityType,\n assigneeRole,\n supervisoryRole,\n input\n } = activityInstance\n\n var origin = id\n ? await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id },\n relations: [\n 'domain',\n 'activity',\n 'assigneeRole',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\n })\n : null\n\n /* \n Prerequisites for a Task to Be Issued.\n - The previous state of the task should be Draft.\n */\n if (origin && origin.state !== ActivityInstanceStatus.Draft) {\n throw new Error(\n context.t(`error.activity-instance is already issued`, {\n id: id,\n state: origin.state\n })\n )\n }\n\n var repository = tx.getRepository(Activity)\n var activity =\n origin?.activity ||\n (await repository.findOne({\n where: { domain: domain.parentId ? { id: In([domain.id, domain.parentId]) } : { id: domain.id }, id: activityId },\n relations: ['assigneeRole', 'supervisoryRole']\n }))\n\n if (!activity) {\n throw new Error(\n context.t('error.activity not found', {\n activity: activityId\n })\n )\n }\n\n if (!origin) {\n activityInstance.name = name || activity.name\n activityInstance.description = description || activity.description\n activityInstance.activityType = activityType || activity.activityType\n activityInstance.uiType = uiType || activity.uiType\n activityInstance.uiSource = uiSource || activity.uiSource\n activityInstance.viewType = viewType || activity.viewType\n activityInstance.viewSource = viewSource || activity.viewSource\n activityInstance.assigneeRole = assigneeRole || activity.assigneeRole\n activityInstance.supervisoryRole = supervisoryRole || activity.supervisoryRole\n activityInstance.priority = priority ?? activity.priority\n\n activityInstance.adhocType = 'standard'\n activityInstance.refBy = activityId\n\n /*\n * 낸 쪽이 직접 주지 않았으면 액티비티의 선언을 따른다.\n *\n * 위의 `if (!assignees || !approvalLine)` 안에 두지 않는다 — 둘 다 준 건에서는 그 블록에 안 들어가고,\n * 그러면 이 축만 조용히 빠진다.\n */\n if (activityInstance.rejectionIsFinal === undefined || activityInstance.rejectionIsFinal === null) {\n activityInstance.rejectionIsFinal = activity.rejectionIsFinal ?? false\n }\n\n if (!assignees || !approvalLine) {\n if (!assignees) {\n assignees = activityInstance.assignees = activity.assignees\n }\n\n if (!approvalLine) {\n approvalLine = activityInstance.approvalLine = activity.approvalLine\n }\n\n if (!dueAt && activity.standardTime) {\n activityInstance.dueAt = new Date(issuedAt.getTime() + activity.standardTime * 1000)\n }\n }\n }\n\n /*\n * **결재를 받겠다고 낸 건은 결재선이 있어야 한다.**\n *\n * 액티비티의 결재선을 물려받은 뒤에 본다 — 낸 쪽이 직접 주지 않아도 액티비티가 들고 있으면 그것이\n * 이 건의 결재선이다. 물려받기 전에 보면 멀쩡한 건을 막는다.\n *\n * 초안을 다시 내는 경우 결재선은 원본이 들고 있으므로 그것까지 함께 본다.\n *\n * 판단은 `approval-expectation.ts` 가 든다 — 여기서 조건을 적으면 시험할 수 없다.\n */\n const expectation = judgeApprovalExpectation({\n requireApproval: activityInstance.requireApproval,\n approvalLine: activityInstance.approvalLine ?? origin?.approvalLine,\n activityName: activity.name\n })\n\n if (expectationBroken(expectation)) {\n throw new Error(\n context.t('error.approval line is empty but approval was required', {\n activity: expectation.activityName,\n defaultValue:\n `\"${expectation.activityName}\" 에 결재선이 없습니다. 이 건은 결재를 받기로 하고 낸 것이라 올리지 않습니다 — ` +\n '그대로 두면 결재 없이 확정됩니다.'\n })\n )\n }\n\n const activitySearchKeys = fillActivitySearchKeys(activity?.searchKeys, input)\n\n if (activity.startingType == 'post') {\n /* \n startingType이 'post'이면, \n activityInstance 이슈와 동시에 issuer에게 할당된 activityThread가 생성되고 submit까지 모두 완료되게 된다.\n 만일, 결재선이 있다면, 상신되게 된다.\n */\n const posted = await tx.getRepository(ActivityInstance).save({\n version: 1,\n creator: user,\n ...origin,\n ...activityInstance,\n transaction: 'post',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Started,\n domain,\n issuer: user,\n updater: user,\n issuedAt,\n startedAt: issuedAt\n })\n\n const thread = await createActivityThreadForUser('post', posted, user, context)\n\n /* post 방식은 thread가 하나이므로, instance의 output을 그대로 thread의 output이라고 판단할 수 있다. 따라서, 아래 로직은 제거한다. */\n // const data = { ...activityInstance.input, ...activityInstance.output }\n // const output = (activity.model || [])\n // .filter(item => item.inout === 'inout' || item.inout === 'out')\n // .reduce((inout, item) => {\n // inout[item.tag] = data[item.tag]\n // return inout\n // }, {})\n\n await submit({ id: thread.id, output: activityInstance.output }, context)\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: posted.id })\n } else {\n const issued = await tx.getRepository(ActivityInstance).save({\n version: 1,\n creator: user,\n ...origin,\n ...activityInstance,\n transaction: 'issue',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Issued,\n domain,\n issuer: user,\n updater: user,\n issuedAt\n })\n\n /*\n 1. 하나의 thread가 필요한 경우 (대부분)\n 조건 : activityInstance의 threadsMin이 1 이어야 한다.\n 로직 1 : getSystemUserFromOrgMemberItem 의 결과가 한명이라면 그대로 activityThread.assignee로 직접 할당한다.\n 로직 2 : getSystemUserFromOrgMemberItem 의 결과가 한명이 아니라면, assignee타입이 assigneeRole 인 경우에만 처리가 가능하다.\n 이 경우에는 assignee를 비운다. - createActivityThreadWithUnassigned\n 2. 할당되지 않은 복수개의 thread가 필요한 경우\n 조건 : threadsMin이 1 이상이어야 한다.\n 로직 1 : getSystemUserFromOrgMemberItem 의 결과와 threadsMin 값이 같다면, 그대로 activityThread.assignee로 직접 할당한다. createActivityThreadsForAllRoleUsers\n 로직 2 : createActivityThreadWithUnassigned\n 3. 모든 assignee가 처리를 해야하는 경우\n 조건 : threadsMin이 0 이어야 한다.\n 로직 : getSystemUserFromOrgMemberItem 의 결과로 createActivityThreadsForUsers\n */\n\n // assignees가 복수인 것은 아무 의미가 없다.\n const assignedUsers = await Promise.all(\n (assignees || []).map(assignee => getSystemUserFromOrgMemberItem(assignee, context))\n )\n\n if (assignedUsers.length == 0) {\n if (issued.assigneeRoleId) {\n if (issued.threadsMin === 0) {\n /* assigneeRoleId에 해당하는 모든 assignee 에게 태스크를 할당하는 경우 */\n await createActivityThreadsForAllRoleUsers('issue', issued, context)\n } else {\n /* minimum threads 설정에 따른 pending assignment 상태로 activityThread를 만드는 경우 */\n await createActivityThreadWithUnassigned('issue', issued, context)\n }\n }\n } else {\n await createActivityThreadsForUsers('issue', issued, assignedUsers, context)\n }\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: issued.id })\n }\n}\n"]}
1
+ {"version":3,"file":"issue.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/issue.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Activity } from '../../service/activity/activity.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityInstanceIssue } from '../../service/activity-instance/activity-instance-type.js'\nimport {\n createActivityThreadWithUnassigned,\n createActivityThreadsForAllRoleUsers,\n createActivityThreadsForUsers,\n getSystemUserFromOrgMemberItem,\n createActivityThreadForUser,\n fillActivitySearchKeys\n} from '../common.js'\nimport { submit } from '../activity-thread/submit.js'\nimport { assertMayWorkOnDraft } from './entitlement.js'\nimport { expectationBroken, judgeApprovalExpectation } from './approval-expectation.js'\n\nexport async function issue(\n activityInstance: ActivityInstanceIssue,\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const issuedAt = new Date()\n var {\n id,\n name,\n description,\n assignees,\n uiType,\n uiSource,\n viewType,\n viewSource,\n dueAt,\n priority,\n approvalLine,\n activityId,\n activityType,\n assigneeRole,\n supervisoryRole,\n input\n } = activityInstance\n\n var origin = id\n ? await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id },\n relations: [\n 'domain',\n 'activity',\n 'assigneeRole',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\n })\n : null\n\n /*\n 남의 초안은 못 내보낸다.\n\n 내보내는 순간 결재선과 담당자가 서고, 그것은 **다른 사람들의 의무**가 된다. 새로 시작하는\n 것(id 없음)은 주인이 없으므로 이 검사에 안 걸린다. 자격 규칙은 `entitlement.ts` 에 있다.\n */\n if (origin) {\n await assertMayWorkOnDraft(origin, context)\n }\n\n /* \n Prerequisites for a Task to Be Issued.\n - The previous state of the task should be Draft.\n */\n if (origin && origin.state !== ActivityInstanceStatus.Draft) {\n throw new Error(\n context.t(`error.activity-instance is already issued`, {\n id: id,\n state: origin.state\n })\n )\n }\n\n var repository = tx.getRepository(Activity)\n var activity =\n origin?.activity ||\n (await repository.findOne({\n where: { domain: domain.parentId ? { id: In([domain.id, domain.parentId]) } : { id: domain.id }, id: activityId },\n relations: ['assigneeRole', 'supervisoryRole']\n }))\n\n if (!activity) {\n throw new Error(\n context.t('error.activity not found', {\n activity: activityId\n })\n )\n }\n\n if (!origin) {\n activityInstance.name = name || activity.name\n activityInstance.description = description || activity.description\n activityInstance.activityType = activityType || activity.activityType\n activityInstance.uiType = uiType || activity.uiType\n activityInstance.uiSource = uiSource || activity.uiSource\n activityInstance.viewType = viewType || activity.viewType\n activityInstance.viewSource = viewSource || activity.viewSource\n activityInstance.assigneeRole = assigneeRole || activity.assigneeRole\n activityInstance.supervisoryRole = supervisoryRole || activity.supervisoryRole\n activityInstance.priority = priority ?? activity.priority\n\n activityInstance.adhocType = 'standard'\n activityInstance.refBy = activityId\n\n /*\n * 낸 쪽이 직접 주지 않았으면 액티비티의 선언을 따른다.\n *\n * 위의 `if (!assignees || !approvalLine)` 안에 두지 않는다 — 둘 다 준 건에서는 그 블록에 안 들어가고,\n * 그러면 이 축만 조용히 빠진다.\n */\n if (activityInstance.rejectionIsFinal === undefined || activityInstance.rejectionIsFinal === null) {\n activityInstance.rejectionIsFinal = activity.rejectionIsFinal ?? false\n }\n\n if (!assignees || !approvalLine) {\n if (!assignees) {\n assignees = activityInstance.assignees = activity.assignees\n }\n\n if (!approvalLine) {\n approvalLine = activityInstance.approvalLine = activity.approvalLine\n }\n\n if (!dueAt && activity.standardTime) {\n activityInstance.dueAt = new Date(issuedAt.getTime() + activity.standardTime * 1000)\n }\n }\n }\n\n /*\n * **결재를 받겠다고 낸 건은 결재선이 있어야 한다.**\n *\n * 액티비티의 결재선을 물려받은 뒤에 본다 — 낸 쪽이 직접 주지 않아도 액티비티가 들고 있으면 그것이\n * 이 건의 결재선이다. 물려받기 전에 보면 멀쩡한 건을 막는다.\n *\n * 초안을 다시 내는 경우 결재선은 원본이 들고 있으므로 그것까지 함께 본다.\n *\n * 판단은 `approval-expectation.ts` 가 든다 — 여기서 조건을 적으면 시험할 수 없다.\n */\n const expectation = judgeApprovalExpectation({\n requireApproval: activityInstance.requireApproval,\n approvalLine: activityInstance.approvalLine ?? origin?.approvalLine,\n activityName: activity.name\n })\n\n if (expectationBroken(expectation)) {\n throw new Error(\n context.t('error.approval line is empty but approval was required', {\n activity: expectation.activityName,\n defaultValue:\n `\"${expectation.activityName}\" 에 결재선이 없습니다. 이 건은 결재를 받기로 하고 낸 것이라 올리지 않습니다 — ` +\n '그대로 두면 결재 없이 확정됩니다.'\n })\n )\n }\n\n const activitySearchKeys = fillActivitySearchKeys(activity?.searchKeys, input)\n\n if (activity.startingType == 'post') {\n /* \n startingType이 'post'이면, \n activityInstance 이슈와 동시에 issuer에게 할당된 activityThread가 생성되고 submit까지 모두 완료되게 된다.\n 만일, 결재선이 있다면, 상신되게 된다.\n */\n const posted = await tx.getRepository(ActivityInstance).save({\n version: 1,\n creator: user,\n ...origin,\n ...activityInstance,\n transaction: 'post',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Started,\n domain,\n issuer: user,\n updater: user,\n issuedAt,\n startedAt: issuedAt\n })\n\n const thread = await createActivityThreadForUser('post', posted, user, context)\n\n /* post 방식은 thread가 하나이므로, instance의 output을 그대로 thread의 output이라고 판단할 수 있다. 따라서, 아래 로직은 제거한다. */\n // const data = { ...activityInstance.input, ...activityInstance.output }\n // const output = (activity.model || [])\n // .filter(item => item.inout === 'inout' || item.inout === 'out')\n // .reduce((inout, item) => {\n // inout[item.tag] = data[item.tag]\n // return inout\n // }, {})\n\n await submit({ id: thread.id, output: activityInstance.output }, context)\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: posted.id })\n } else {\n const issued = await tx.getRepository(ActivityInstance).save({\n version: 1,\n creator: user,\n ...origin,\n ...activityInstance,\n transaction: 'issue',\n activity,\n ...activitySearchKeys,\n state: ActivityInstanceStatus.Issued,\n domain,\n issuer: user,\n updater: user,\n issuedAt\n })\n\n /*\n 1. 하나의 thread가 필요한 경우 (대부분)\n 조건 : activityInstance의 threadsMin이 1 이어야 한다.\n 로직 1 : getSystemUserFromOrgMemberItem 의 결과가 한명이라면 그대로 activityThread.assignee로 직접 할당한다.\n 로직 2 : getSystemUserFromOrgMemberItem 의 결과가 한명이 아니라면, assignee타입이 assigneeRole 인 경우에만 처리가 가능하다.\n 이 경우에는 assignee를 비운다. - createActivityThreadWithUnassigned\n 2. 할당되지 않은 복수개의 thread가 필요한 경우\n 조건 : threadsMin이 1 이상이어야 한다.\n 로직 1 : getSystemUserFromOrgMemberItem 의 결과와 threadsMin 값이 같다면, 그대로 activityThread.assignee로 직접 할당한다. createActivityThreadsForAllRoleUsers\n 로직 2 : createActivityThreadWithUnassigned\n 3. 모든 assignee가 처리를 해야하는 경우\n 조건 : threadsMin이 0 이어야 한다.\n 로직 : getSystemUserFromOrgMemberItem 의 결과로 createActivityThreadsForUsers\n */\n\n // assignees가 복수인 것은 아무 의미가 없다.\n const assignedUsers = await Promise.all(\n (assignees || []).map(assignee => getSystemUserFromOrgMemberItem(assignee, context))\n )\n\n if (assignedUsers.length == 0) {\n if (issued.assigneeRoleId) {\n if (issued.threadsMin === 0) {\n /* assigneeRoleId에 해당하는 모든 assignee 에게 태스크를 할당하는 경우 */\n await createActivityThreadsForAllRoleUsers('issue', issued, context)\n } else {\n /* minimum threads 설정에 따른 pending assignment 상태로 activityThread를 만드는 경우 */\n await createActivityThreadWithUnassigned('issue', issued, context)\n }\n }\n } else {\n await createActivityThreadsForUsers('issue', issued, assignedUsers, context)\n }\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: issued.id })\n }\n}\n"]}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.pick = pick;
4
4
  const activity_instance_js_1 = require("../../service/activity-instance/activity-instance.js");
5
5
  const common_js_1 = require("../common.js");
6
+ const entitlement_js_1 = require("./entitlement.js");
6
7
  async function pick(id, context) {
7
8
  const { domain, user, tx } = context.state;
8
9
  const repository = tx.getRepository(activity_instance_js_1.ActivityInstance);
@@ -15,6 +16,13 @@ async function pick(id, context) {
15
16
  activityInstance: id
16
17
  }));
17
18
  }
19
+ /*
20
+ 누가 집을 수 있나 — 상태만 보면 **아무나 집는다.**
21
+
22
+ `assigneeRole` 을 relations 로 불러오면서 한 번도 안 봤다. 그래서 그 도메인에 로그인한
23
+ 사람이면 특정 역할에 걸어 둔 업무도 집을 수 있었다. 자격 규칙은 `entitlement.ts` 에 있다.
24
+ */
25
+ await (0, entitlement_js_1.assertMayPick)(activityInstance, context);
18
26
  /*
19
27
  Prerequisites for a Task to Be Picked.
20
28
  - The previous state of the task must not be Assigned.
@@ -1 +1 @@
1
- {"version":3,"file":"pick.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/pick.ts"],"names":[],"mappings":";;AAIA,oBAwCC;AA5CD,+FAA+G;AAE/G,4CAA4D;AAErD,KAAK,UAAU,IAAI,CAAC,EAAU,EAAE,OAAwB;IAC7D,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAA;IAErD,IAAI,gBAAgB,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC;QAC9C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACxC,SAAS,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC;KACpH,CAAC,CAAA;IAEF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,mCAAmC,EAAE;YAC7C,gBAAgB,EAAE,EAAE;SACrB,CAAC,CACH,CAAA;IACH,CAAC;IAED;;;MAGE;IACF,IACE,gBAAgB,CAAC,KAAK,KAAK,6CAAsB,CAAC,MAAM;QACxD,gBAAgB,CAAC,KAAK,KAAK,6CAAsB,CAAC,UAAU,EAC5D,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,gDAAgD,EAAE;YAC1D,EAAE;YACF,MAAM,EAAE,gBAAgB,CAAC,KAAK;SAC/B,CAAC,CACH,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC;QACnC,GAAG,gBAAgB;QACnB,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,IAAI;KACd,CAAC,CAAA;IAEF,OAAO,CAAC,MAAM,IAAA,yCAA6B,EAAC,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAClF,CAAC","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThread } from '../../service/activity-thread/activity-thread.js'\nimport { createActivityThreadsForUsers } from '../common.js'\n\nexport async function pick(id: string, context: ResolverContext): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(ActivityInstance)\n\n var activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activity', 'assigneeRole', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']\n })\n\n if (!activityInstance) {\n throw new Error(\n context.t('error.activity-instance not found', {\n activityInstance: id\n })\n )\n }\n\n /* \n Prerequisites for a Task to Be Picked.\n - The previous state of the task must not be Assigned.\n */\n if (\n activityInstance.state !== ActivityInstanceStatus.Issued &&\n activityInstance.state !== ActivityInstanceStatus.Unassigned\n ) {\n throw new Error(\n context.t(`error.activity-instance should not be assigned`, {\n id,\n actual: activityInstance.state\n })\n )\n }\n\n const picked = await repository.save({\n ...activityInstance,\n transaction: 'pick',\n updater: user\n })\n\n return (await createActivityThreadsForUsers('pick', picked, [user], context))[0]\n}\n"]}
1
+ {"version":3,"file":"pick.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/pick.ts"],"names":[],"mappings":";;AAKA,oBAgDC;AArDD,+FAA+G;AAE/G,4CAA4D;AAC5D,qDAAgD;AAEzC,KAAK,UAAU,IAAI,CAAC,EAAU,EAAE,OAAwB;IAC7D,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAA;IAErD,IAAI,gBAAgB,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC;QAC9C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACxC,SAAS,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC;KACpH,CAAC,CAAA;IAEF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,mCAAmC,EAAE;YAC7C,gBAAgB,EAAE,EAAE;SACrB,CAAC,CACH,CAAA;IACH,CAAC;IAED;;;;;MAKE;IACF,MAAM,IAAA,8BAAa,EAAC,gBAAgB,EAAE,OAAO,CAAC,CAAA;IAE9C;;;MAGE;IACF,IACE,gBAAgB,CAAC,KAAK,KAAK,6CAAsB,CAAC,MAAM;QACxD,gBAAgB,CAAC,KAAK,KAAK,6CAAsB,CAAC,UAAU,EAC5D,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,gDAAgD,EAAE;YAC1D,EAAE;YACF,MAAM,EAAE,gBAAgB,CAAC,KAAK;SAC/B,CAAC,CACH,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC;QACnC,GAAG,gBAAgB;QACnB,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,IAAI;KACd,CAAC,CAAA;IAEF,OAAO,CAAC,MAAM,IAAA,yCAA6B,EAAC,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAClF,CAAC","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThread } from '../../service/activity-thread/activity-thread.js'\nimport { createActivityThreadsForUsers } from '../common.js'\nimport { assertMayPick } from './entitlement.js'\n\nexport async function pick(id: string, context: ResolverContext): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(ActivityInstance)\n\n var activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activity', 'assigneeRole', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']\n })\n\n if (!activityInstance) {\n throw new Error(\n context.t('error.activity-instance not found', {\n activityInstance: id\n })\n )\n }\n\n /*\n 누가 집을 수 있나 — 상태만 보면 **아무나 집는다.**\n\n `assigneeRole` 을 relations 로 불러오면서 한 번도 안 봤다. 그래서 그 도메인에 로그인한\n 사람이면 특정 역할에 걸어 둔 업무도 집을 수 있었다. 자격 규칙은 `entitlement.ts` 에 있다.\n */\n await assertMayPick(activityInstance, context)\n\n /* \n Prerequisites for a Task to Be Picked.\n - The previous state of the task must not be Assigned.\n */\n if (\n activityInstance.state !== ActivityInstanceStatus.Issued &&\n activityInstance.state !== ActivityInstanceStatus.Unassigned\n ) {\n throw new Error(\n context.t(`error.activity-instance should not be assigned`, {\n id,\n actual: activityInstance.state\n })\n )\n }\n\n const picked = await repository.save({\n ...activityInstance,\n transaction: 'pick',\n updater: user\n })\n\n return (await createActivityThreadsForUsers('pick', picked, [user], context))[0]\n}\n"]}
@@ -5,5 +5,5 @@ import { ActivityQuery } from "./activity-query.js";
5
5
  import { ActivityHistoryEntitySubscriber } from "./event-subscriber.js";
6
6
  import { ActivityDeclarationGuardSubscriber } from "./declaration-guard-subscriber.js";
7
7
  export declare const entities: (typeof Activity | typeof ActivityHistory)[];
8
- export declare const resolvers: (typeof ActivityQuery | typeof ActivityMutation)[];
8
+ export declare const resolvers: (typeof ActivityMutation | typeof ActivityQuery)[];
9
9
  export declare const subscribers: (typeof ActivityHistoryEntitySubscriber | typeof ActivityDeclarationGuardSubscriber)[];
@@ -32,6 +32,23 @@ let ActivityInstanceMutation = class ActivityInstanceMutation {
32
32
  });
33
33
  return true;
34
34
  }
35
+ /*
36
+ * 이 셋(draft · issue · pick)에는 `@privilege` 를 안 단다 — **일부러다.**
37
+ *
38
+ * 그 권한(`activity-instance:mutation` + domainOwnerGranted)은 **관리 권한**이다. 「내게 온
39
+ * 일을 집는다」에 그것을 요구하면 현장 작업자 전원에게 남의 일을 재배정하고 중단시킬 권한을
40
+ * 함께 줘야 한다. 그래서 문을 범주로 막지 않는다.
41
+ *
42
+ * 게이트 있음 남의 일에 손대는 것 · 되돌릴 수 없는 것 assign · end · abort · delete draft
43
+ * 게이트 없음 자기 일을 하는 것 draft · issue · pick
44
+ *
45
+ * 대신 **건별 자격**을 본다(`controllers/activity-instance/entitlement.ts`) — 남의 초안은 못
46
+ * 고치고 못 내보내며, 역할이 걸린 업무는 그 역할을 가진 사람만 집는다.
47
+ *
48
+ * 2025-04-10(`74acc61f6d`)에 나머지 여섯에 게이트를 달면서 이 셋만 주석으로 남겼는데, **그
49
+ * 이유가 어디에도 안 적혀 있어서** 읽는 사람이 「일부러」와 「빠뜨림」을 구별할 수 없었다.
50
+ * 주석 처리된 줄은 지운다 — 죽은 줄이 남아 있는 한 다음 사람이 또 같은 것을 묻는다.
51
+ */
35
52
  async draftActivityInstance(activityInstance, context) {
36
53
  return await (0, index_js_1.draft)(activityInstance, context);
37
54
  }
@@ -90,9 +107,7 @@ tslib_1.__decorate([
90
107
  tslib_1.__metadata("design:returntype", Promise)
91
108
  ], ActivityInstanceMutation.prototype, "deleteDraftActivityInstances", null);
92
109
  tslib_1.__decorate([
93
- (0, type_graphql_1.Directive)('@transaction')
94
- // @Directive('@privilege(category: "activity-instance", privilege: "mutation", domainOwnerGranted: true)')
95
- ,
110
+ (0, type_graphql_1.Directive)('@transaction'),
96
111
  (0, type_graphql_1.Mutation)(returns => activity_instance_js_1.ActivityInstance, { description: 'To draft standard ActivityInstance' }),
97
112
  tslib_1.__param(0, (0, type_graphql_1.Arg)('activityInstance')),
98
113
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
@@ -101,9 +116,7 @@ tslib_1.__decorate([
101
116
  tslib_1.__metadata("design:returntype", Promise)
102
117
  ], ActivityInstanceMutation.prototype, "draftActivityInstance", null);
103
118
  tslib_1.__decorate([
104
- (0, type_graphql_1.Directive)('@transaction')
105
- // @Directive('@privilege(category: "activity-instance", privilege: "mutation", domainOwnerGranted: true)')
106
- ,
119
+ (0, type_graphql_1.Directive)('@transaction'),
107
120
  (0, type_graphql_1.Mutation)(returns => activity_instance_js_1.ActivityInstance, { description: 'To issue standard ActivityInstance' }),
108
121
  tslib_1.__param(0, (0, type_graphql_1.Arg)('activityInstance')),
109
122
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
@@ -112,9 +125,7 @@ tslib_1.__decorate([
112
125
  tslib_1.__metadata("design:returntype", Promise)
113
126
  ], ActivityInstanceMutation.prototype, "issueActivityInstance", null);
114
127
  tslib_1.__decorate([
115
- (0, type_graphql_1.Directive)('@transaction')
116
- // @Directive('@privilege(category: "activity-instance", privilege: "mutation", domainOwnerGranted: true)')
117
- ,
128
+ (0, type_graphql_1.Directive)('@transaction'),
118
129
  (0, type_graphql_1.Mutation)(returns => activity_thread_js_1.ActivityThread, {
119
130
  nullable: true,
120
131
  description: 'To pick an activity ActivityInstance voluntarily. [cautions] This resolver will return a assigned ActivityThread.'
@@ -1 +1 @@
1
- {"version":3,"file":"activity-instance-mutation.js","sourceRoot":"","sources":["../../../server/service/activity-instance/activity-instance-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { abort, assign, draft, end, pick, issue } from '../../controllers/activity-instance/index.js'\nimport { ActivityThread } from '../activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from './activity-instance.js'\nimport { ActivityInstanceIssue, ActivityInstanceDraft } from './activity-instance-type.js'\n\n@Resolver(ActivityInstance)\nexport class ActivityInstanceMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete Activity Instance of draft state' })\n async deleteDraftActivityInstance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n let draftActInst = await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id, state: ActivityInstanceStatus.Draft }\n })\n\n if (draftActInst) {\n await tx.getRepository(ActivityInstance).remove(draftActInst)\n return true\n } else {\n return false\n }\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple Activity Instances of draft state' })\n async deleteDraftActivityInstances(\n @Arg('ids', type => [String]) ids: string[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n\n await tx.getRepository(ActivityInstance).delete({\n domain: { id: domain.id },\n id: In(ids),\n state: ActivityInstanceStatus.Draft\n })\n\n return true\n }\n\n @Directive('@transaction')\n // @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To draft standard ActivityInstance' })\n async draftActivityInstance(\n @Arg('activityInstance') activityInstance: ActivityInstanceDraft,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await draft(activityInstance, context)\n }\n\n @Directive('@transaction')\n // @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To issue standard ActivityInstance' })\n async issueActivityInstance(\n @Arg('activityInstance') activityInstance: ActivityInstanceIssue,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await issue(activityInstance, context)\n }\n\n @Directive('@transaction')\n // @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityThread!, {\n nullable: true,\n description:\n 'To pick an activity ActivityInstance voluntarily. [cautions] This resolver will return a assigned ActivityThread.'\n })\n async pickActivityInstance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<ActivityThread> {\n return await pick(id, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To assign a ActivityInstance' })\n async assignActivityInstance(\n @Arg('id') id: string,\n @Arg('assignees', type => [String], { nullable: true, description: 'Email of assignee users' }) assignees: string[],\n @Arg('reason', { nullable: true }) reason: string,\n @Arg('dueAt', { nullable: true }) dueAt: Date,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await assign(\n {\n id,\n assignees,\n reason,\n dueAt\n },\n context\n )\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To end a ActivityInstance' })\n async endActivityInstance(\n @Arg('id') id: string,\n @Arg('output', type => ScalarObject, { nullable: true }) output: { [key: string]: any },\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await end({ id, output, reason }, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To abort a ActivityInstance' })\n async abortActivityInstance(\n @Arg('id') id: string,\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await abort({ id, reason }, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To abort multiple ActivityInstances' })\n async abortActivityInstances(\n @Arg('ids', type => [String]) ids: string[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n for (const id of ids) {\n try {\n await abort({ id, reason: 'Aborted by user' }, context)\n } catch (error) {\n console.error(`Failed to abort activity instance ${id}:`, error)\n return false\n }\n }\n\n return true\n }\n}\n"]}
1
+ {"version":3,"file":"activity-instance-mutation.js","sourceRoot":"","sources":["../../../server/service/activity-instance/activity-instance-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { abort, assign, draft, end, pick, issue } from '../../controllers/activity-instance/index.js'\nimport { ActivityThread } from '../activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from './activity-instance.js'\nimport { ActivityInstanceIssue, ActivityInstanceDraft } from './activity-instance-type.js'\n\n@Resolver(ActivityInstance)\nexport class ActivityInstanceMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete Activity Instance of draft state' })\n async deleteDraftActivityInstance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n let draftActInst = await tx.getRepository(ActivityInstance).findOne({\n where: { domain: { id: domain.id }, id, state: ActivityInstanceStatus.Draft }\n })\n\n if (draftActInst) {\n await tx.getRepository(ActivityInstance).remove(draftActInst)\n return true\n } else {\n return false\n }\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple Activity Instances of draft state' })\n async deleteDraftActivityInstances(\n @Arg('ids', type => [String]) ids: string[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n\n await tx.getRepository(ActivityInstance).delete({\n domain: { id: domain.id },\n id: In(ids),\n state: ActivityInstanceStatus.Draft\n })\n\n return true\n }\n\n /*\n * 이 셋(draft · issue · pick)에는 `@privilege` 를 안 단다 — **일부러다.**\n *\n * 그 권한(`activity-instance:mutation` + domainOwnerGranted)은 **관리 권한**이다. 「내게 온\n * 일을 집는다」에 그것을 요구하면 현장 작업자 전원에게 남의 일을 재배정하고 중단시킬 권한을\n * 함께 줘야 한다. 그래서 문을 범주로 막지 않는다.\n *\n * 게이트 있음 남의 일에 손대는 것 · 되돌릴 수 없는 것 assign · end · abort · delete draft\n * 게이트 없음 자기 일을 하는 것 draft · issue · pick\n *\n * 대신 **건별 자격**을 본다(`controllers/activity-instance/entitlement.ts`) — 남의 초안은 못\n * 고치고 못 내보내며, 역할이 걸린 업무는 그 역할을 가진 사람만 집는다.\n *\n * 2025-04-10(`74acc61f6d`)에 나머지 여섯에 게이트를 달면서 이 셋만 주석으로 남겼는데, **그\n * 이유가 어디에도 안 적혀 있어서** 읽는 사람이 「일부러」와 「빠뜨림」을 구별할 수 없었다.\n * 주석 처리된 줄은 지운다 — 죽은 줄이 남아 있는 한 다음 사람이 또 같은 것을 묻는다.\n */\n @Directive('@transaction')\n @Mutation(returns => ActivityInstance, { description: 'To draft standard ActivityInstance' })\n async draftActivityInstance(\n @Arg('activityInstance') activityInstance: ActivityInstanceDraft,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await draft(activityInstance, context)\n }\n\n @Directive('@transaction')\n @Mutation(returns => ActivityInstance, { description: 'To issue standard ActivityInstance' })\n async issueActivityInstance(\n @Arg('activityInstance') activityInstance: ActivityInstanceIssue,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await issue(activityInstance, context)\n }\n\n @Directive('@transaction')\n @Mutation(returns => ActivityThread!, {\n nullable: true,\n description:\n 'To pick an activity ActivityInstance voluntarily. [cautions] This resolver will return a assigned ActivityThread.'\n })\n async pickActivityInstance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<ActivityThread> {\n return await pick(id, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To assign a ActivityInstance' })\n async assignActivityInstance(\n @Arg('id') id: string,\n @Arg('assignees', type => [String], { nullable: true, description: 'Email of assignee users' }) assignees: string[],\n @Arg('reason', { nullable: true }) reason: string,\n @Arg('dueAt', { nullable: true }) dueAt: Date,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await assign(\n {\n id,\n assignees,\n reason,\n dueAt\n },\n context\n )\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To end a ActivityInstance' })\n async endActivityInstance(\n @Arg('id') id: string,\n @Arg('output', type => ScalarObject, { nullable: true }) output: { [key: string]: any },\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await end({ id, output, reason }, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => ActivityInstance, { description: 'To abort a ActivityInstance' })\n async abortActivityInstance(\n @Arg('id') id: string,\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<ActivityInstance> {\n return await abort({ id, reason }, context)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"activity-instance\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To abort multiple ActivityInstances' })\n async abortActivityInstances(\n @Arg('ids', type => [String]) ids: string[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n for (const id of ids) {\n try {\n await abort({ id, reason: 'Aborted by user' }, context)\n } catch (error) {\n console.error(`Failed to abort activity instance ${id}:`, error)\n return false\n }\n }\n\n return true\n }\n}\n"]}
@@ -185,7 +185,7 @@ let ActivityInstanceQuery = class ActivityInstanceQuery {
185
185
  var approvalLineItems = [];
186
186
  for (let item of approvalLine) {
187
187
  // "value" deprecated. to be removed
188
- var { type, approver, value } = item;
188
+ var { type, approver, value, participation, notifyAfter } = item;
189
189
  var id = approver?.id || value;
190
190
  approver = null;
191
191
  switch (type) {
@@ -206,7 +206,30 @@ let ActivityInstanceQuery = class ActivityInstanceQuery {
206
206
  break;
207
207
  default:
208
208
  }
209
- id ? approvalLineItems.push({ type, approver }) : approvalLineItems.push({ type });
209
+ /*
210
+ * What the seat is, not only who sits in it.
211
+ *
212
+ * This rebuilt each item as { type, approver } and dropped everything else, so `participation`
213
+ * never left the server. The screen asked for it, the query carried it, and the resolver took
214
+ * it out in the middle — the approval screen could not tell a reference seat from an approver
215
+ * and drew approve and reject for both. organization's approval-line-brief lost the same field
216
+ * and drew a reference with a turn number it does not have.
217
+ *
218
+ * The server guard was unaffected, which is why this stayed hidden: approve() and reject()
219
+ * read the entity through TypeORM, not through GraphQL, so they refused correctly while the
220
+ * screen showed buttons that would be refused. Measured 2026-09-11 on a reference row whose
221
+ * stored line said "participation":"reference" and whose GraphQL answer said nothing.
222
+ *
223
+ * `value` stays out — it is the deprecated half of the pair that `approver` replaced.
224
+ */
225
+ const seat = id ? { type, approver } : { type };
226
+ if (participation !== undefined) {
227
+ seat.participation = participation;
228
+ }
229
+ if (notifyAfter !== undefined) {
230
+ seat.notifyAfter = notifyAfter;
231
+ }
232
+ approvalLineItems.push(seat);
210
233
  }
211
234
  return approvalLineItems;
212
235
  }