@things-factory/worklist 10.1.198 → 10.1.203

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 (124) hide show
  1. package/dist-client/components/activity-thread-timeline.d.ts +2 -2
  2. package/dist-client/pages/activity/activity-list-page.d.ts +3 -0
  3. package/dist-client/pages/activity/activity-list-page.js +25 -13
  4. package/dist-client/pages/activity/activity-list-page.js.map +1 -1
  5. package/dist-client/pages/activity/activity-partial-view.js +1 -1
  6. package/dist-client/pages/activity/activity-partial-view.js.map +1 -1
  7. package/dist-client/pages/activity/assignee-roles-cell.d.ts +32 -0
  8. package/dist-client/pages/activity/assignee-roles-cell.js +26 -0
  9. package/dist-client/pages/activity/assignee-roles-cell.js.map +1 -0
  10. package/dist-client/pages/activity-catalog/activity-catalog-page.js +1 -1
  11. package/dist-client/pages/activity-catalog/activity-catalog-page.js.map +1 -1
  12. package/dist-client/pages/activity-instance/activity-instance-view.d.ts +1 -1
  13. package/dist-client/pages/activity-thread/activity-thread-view.d.ts +1 -1
  14. package/dist-client/pages/work-door/door-sentence.d.ts +1 -1
  15. package/dist-client/pages/work-door/door-sentence.js +2 -2
  16. package/dist-client/pages/work-door/door-sentence.js.map +1 -1
  17. package/dist-client/route.d.ts +1 -1
  18. package/dist-client/tsconfig.tsbuildinfo +1 -1
  19. package/dist-client/types/activity-instance-type.d.ts +2 -2
  20. package/dist-client/types/activity-instance-type.js.map +1 -1
  21. package/dist-client/types/activity-instance.d.ts +1 -1
  22. package/dist-client/types/activity-instance.js.map +1 -1
  23. package/dist-client/types/activity.d.ts +1 -1
  24. package/dist-client/types/activity.js.map +1 -1
  25. package/dist-server/controllers/activity-approval/resolve-judgeable-seats.d.ts +2 -2
  26. package/dist-server/controllers/activity-approval/resolve-judgeable-seats.js +2 -2
  27. package/dist-server/controllers/activity-approval/resolve-judgeable-seats.js.map +1 -1
  28. package/dist-server/controllers/activity-instance/abort.js +1 -1
  29. package/dist-server/controllers/activity-instance/abort.js.map +1 -1
  30. package/dist-server/controllers/activity-instance/assign.d.ts +2 -1
  31. package/dist-server/controllers/activity-instance/assign.js +104 -2
  32. package/dist-server/controllers/activity-instance/assign.js.map +1 -1
  33. package/dist-server/controllers/activity-instance/assignee-input.d.ts +25 -0
  34. package/dist-server/controllers/activity-instance/assignee-input.js +28 -0
  35. package/dist-server/controllers/activity-instance/assignee-input.js.map +1 -0
  36. package/dist-server/controllers/activity-instance/draft.js +7 -2
  37. package/dist-server/controllers/activity-instance/draft.js.map +1 -1
  38. package/dist-server/controllers/activity-instance/end.js +1 -1
  39. package/dist-server/controllers/activity-instance/end.js.map +1 -1
  40. package/dist-server/controllers/activity-instance/entitlement.d.ts +1 -1
  41. package/dist-server/controllers/activity-instance/entitlement.js +7 -5
  42. package/dist-server/controllers/activity-instance/entitlement.js.map +1 -1
  43. package/dist-server/controllers/activity-instance/issue.js +28 -7
  44. package/dist-server/controllers/activity-instance/issue.js.map +1 -1
  45. package/dist-server/controllers/activity-instance/pick.js +22 -3
  46. package/dist-server/controllers/activity-instance/pick.js.map +1 -1
  47. package/dist-server/controllers/activity-read/assert-may-read.js +2 -1
  48. package/dist-server/controllers/activity-read/assert-may-read.js.map +1 -1
  49. package/dist-server/controllers/activity-read/participates.d.ts +2 -2
  50. package/dist-server/controllers/activity-read/participates.js +2 -2
  51. package/dist-server/controllers/activity-read/participates.js.map +1 -1
  52. package/dist-server/controllers/activity-thread/check-authority.js +22 -4
  53. package/dist-server/controllers/activity-thread/check-authority.js.map +1 -1
  54. package/dist-server/controllers/common.js +17 -12
  55. package/dist-server/controllers/common.js.map +1 -1
  56. package/dist-server/service/activity/activity-history.d.ts +1 -2
  57. package/dist-server/service/activity/activity-history.js +4 -8
  58. package/dist-server/service/activity/activity-history.js.map +1 -1
  59. package/dist-server/service/activity/activity-mutation.js +39 -8
  60. package/dist-server/service/activity/activity-mutation.js.map +1 -1
  61. package/dist-server/service/activity/activity-query.d.ts +1 -1
  62. package/dist-server/service/activity/activity-query.js +9 -9
  63. package/dist-server/service/activity/activity-query.js.map +1 -1
  64. package/dist-server/service/activity/activity-type.d.ts +2 -2
  65. package/dist-server/service/activity/activity-type.js +12 -6
  66. package/dist-server/service/activity/activity-type.js.map +1 -1
  67. package/dist-server/service/activity/activity.d.ts +2 -2
  68. package/dist-server/service/activity/activity.js +15 -7
  69. package/dist-server/service/activity/activity.js.map +1 -1
  70. package/dist-server/service/activity/event-subscriber.js.map +1 -1
  71. package/dist-server/service/activity-instance/activity-instance-history.d.ts +1 -2
  72. package/dist-server/service/activity-instance/activity-instance-history.js +4 -8
  73. package/dist-server/service/activity-instance/activity-instance-history.js.map +1 -1
  74. package/dist-server/service/activity-instance/activity-instance-mutation.d.ts +1 -1
  75. package/dist-server/service/activity-instance/activity-instance-mutation.js +10 -5
  76. package/dist-server/service/activity-instance/activity-instance-mutation.js.map +1 -1
  77. package/dist-server/service/activity-instance/activity-instance-query.d.ts +1 -2
  78. package/dist-server/service/activity-instance/activity-instance-query.js +16 -25
  79. package/dist-server/service/activity-instance/activity-instance-query.js.map +1 -1
  80. package/dist-server/service/activity-instance/activity-instance-type.d.ts +2 -2
  81. package/dist-server/service/activity-instance/activity-instance-type.js +12 -6
  82. package/dist-server/service/activity-instance/activity-instance-type.js.map +1 -1
  83. package/dist-server/service/activity-instance/activity-instance.d.ts +2 -2
  84. package/dist-server/service/activity-instance/activity-instance.js +15 -7
  85. package/dist-server/service/activity-instance/activity-instance.js.map +1 -1
  86. package/dist-server/service/activity-instance/assignee-pool.d.ts +59 -0
  87. package/dist-server/service/activity-instance/assignee-pool.js +99 -0
  88. package/dist-server/service/activity-instance/assignee-pool.js.map +1 -0
  89. package/dist-server/service/activity-instance/event-subscriber.d.ts +9 -1
  90. package/dist-server/service/activity-instance/event-subscriber.js +20 -4
  91. package/dist-server/service/activity-instance/event-subscriber.js.map +1 -1
  92. package/dist-server/service/activity-summary/activity-summary-query.js +3 -3
  93. package/dist-server/service/activity-summary/activity-summary-query.js.map +1 -1
  94. package/dist-server/service/activity-thread/my-open-threads.d.ts +1 -1
  95. package/dist-server/service/activity-thread/my-open-threads.js +4 -3
  96. package/dist-server/service/activity-thread/my-open-threads.js.map +1 -1
  97. package/dist-server/service/index.d.ts +1 -1
  98. package/dist-server/service/subscription/subscription-filters.d.ts +3 -2
  99. package/dist-server/service/subscription/subscription-filters.js +6 -3
  100. package/dist-server/service/subscription/subscription-filters.js.map +1 -1
  101. package/dist-server/service/work-door/work-door-service.js +9 -6
  102. package/dist-server/service/work-door/work-door-service.js.map +1 -1
  103. package/dist-server/service/work-door/work-door-type.d.ts +1 -1
  104. package/dist-server/service/work-door/work-door-type.js +4 -4
  105. package/dist-server/service/work-door/work-door-type.js.map +1 -1
  106. package/dist-server/service/work-door/work-door.d.ts +3 -3
  107. package/dist-server/service/work-door/work-door.js +2 -2
  108. package/dist-server/service/work-door/work-door.js.map +1 -1
  109. package/dist-server/tsconfig.tsbuildinfo +1 -1
  110. package/package.json +9 -9
  111. package/tests/assign-role-pool.test.ts +508 -0
  112. package/tests/assignee-pool-history.test.ts +81 -0
  113. package/tests/may-read-case.test.ts +3 -2
  114. package/tests/org-member-domain.test.ts +10 -4
  115. package/tests/pick-race.pg.test.ts +2 -2
  116. package/tests/published-shape.test.ts +2 -2
  117. package/tests/role-pool-race.pg.test.ts +182 -0
  118. package/tests/work-door.test.ts +3 -3
  119. package/translations/en.json +5 -0
  120. package/translations/es.json +5 -0
  121. package/translations/ja.json +5 -0
  122. package/translations/ko.json +5 -0
  123. package/translations/ms.json +5 -0
  124. package/translations/zh.json +5 -0
@@ -21,7 +21,7 @@ export declare class ActivityInstanceDraft {
21
21
  uiType?: ActivityUIType;
22
22
  uiSource?: string;
23
23
  supervisoryRole?: Role;
24
- assigneeRole?: Role;
24
+ assigneeRoles?: Role[];
25
25
  assignees?: AssigneeItem[];
26
26
  approvalLine?: ApprovalLineItem[];
27
27
  }
@@ -44,7 +44,7 @@ export declare class ActivityInstanceIssue {
44
44
  uiType?: ActivityUIType;
45
45
  uiSource?: string;
46
46
  supervisoryRole?: Role;
47
- assigneeRole?: Role;
47
+ assigneeRoles?: Role[];
48
48
  assignees?: AssigneeItem[];
49
49
  approvalLine?: ApprovalLineItem[];
50
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"activity-instance-type.js","sourceRoot":"","sources":["../../client/types/activity-instance-type.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,qBAAqB;CAsCjC;AAED,MAAM,OAAO,qBAAqB;CAsCjC;AAED,MAAM,OAAO,oBAAoB;CAMhC;AAED,MAAM,OAAO,qBAAqB;CAwBjC;AAED,MAAM,OAAO,oBAAoB;CAIhC","sourcesContent":["import { Role } from '@operato/shell'\nimport { ApprovalLineItem } from '@things-factory/organization'\n\nimport { ActivityType, ActivityUIType } from './activity.js'\nimport { AssigneeItem, ActivityInstance, ActivityInstanceStatus } from './activity-instance.js'\n\nexport class ActivityInstanceDraft {\n id?: string\n\n name?: string\n\n activityId?: string\n\n description?: string\n\n priority?: number\n\n reason?: string\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n adhocType?: string\n\n refBy?: string\n\n activityType?: ActivityType\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n supervisoryRole?: Role\n\n assigneeRole?: Role\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstanceIssue {\n id?: string\n\n name?: string\n\n activityId?: string\n\n description?: string\n\n priority?: number\n\n reason?: string\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n adhocType?: string\n\n refBy?: string\n\n activityType?: ActivityType\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n supervisoryRole?: Role\n\n assigneeRole?: Role\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstanceSave {\n output?: { [key: string]: any }\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstancePatch {\n id?: string\n\n name?: string\n\n description?: string\n\n priority?: number\n\n state?: ActivityInstanceStatus\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n cuFlag?: string\n}\n\nexport class ActivityInstanceList {\n items?: ActivityInstance[]\n\n total?: number\n}\n"]}
1
+ {"version":3,"file":"activity-instance-type.js","sourceRoot":"","sources":["../../client/types/activity-instance-type.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,qBAAqB;CAsCjC;AAED,MAAM,OAAO,qBAAqB;CAsCjC;AAED,MAAM,OAAO,oBAAoB;CAMhC;AAED,MAAM,OAAO,qBAAqB;CAwBjC;AAED,MAAM,OAAO,oBAAoB;CAIhC","sourcesContent":["import { Role } from '@operato/shell'\nimport { ApprovalLineItem } from '@things-factory/organization'\n\nimport { ActivityType, ActivityUIType } from './activity.js'\nimport { AssigneeItem, ActivityInstance, ActivityInstanceStatus } from './activity-instance.js'\n\nexport class ActivityInstanceDraft {\n id?: string\n\n name?: string\n\n activityId?: string\n\n description?: string\n\n priority?: number\n\n reason?: string\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n adhocType?: string\n\n refBy?: string\n\n activityType?: ActivityType\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n supervisoryRole?: Role\n\n assigneeRoles?: Role[]\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstanceIssue {\n id?: string\n\n name?: string\n\n activityId?: string\n\n description?: string\n\n priority?: number\n\n reason?: string\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n adhocType?: string\n\n refBy?: string\n\n activityType?: ActivityType\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n supervisoryRole?: Role\n\n assigneeRoles?: Role[]\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstanceSave {\n output?: { [key: string]: any }\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n}\n\nexport class ActivityInstancePatch {\n id?: string\n\n name?: string\n\n description?: string\n\n priority?: number\n\n state?: ActivityInstanceStatus\n\n threadsMin?: number\n\n threadsMax?: number\n\n dueAt?: Date\n\n input?: { [key: string]: any }\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n cuFlag?: string\n}\n\nexport class ActivityInstanceList {\n items?: ActivityInstance[]\n\n total?: number\n}\n"]}
@@ -46,7 +46,7 @@ export declare class ActivityInstance {
46
46
  };
47
47
  refBy?: string;
48
48
  activity?: Activity;
49
- assigneeRole?: Role;
49
+ assigneeRoles?: Role[];
50
50
  supervisoryRole?: Role;
51
51
  dueAt?: Date;
52
52
  transaction?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"activity-instance.js","sourceRoot":"","sources":["../../client/types/activity-instance.ts"],"names":[],"mappings":"AAMA,MAAM,CAAN,IAAY,sBASX;AATD,WAAY,sBAAsB;IAChC,yCAAe,CAAA;IACf,2CAAiB,CAAA;IACjB,mDAAyB,CAAA;IACzB,+CAAqB,CAAA;IACrB,6CAAmB,CAAA;IACnB,6CAAmB,CAAA;IACnB,yCAAe,CAAA;IACf,6CAAmB,CAAA;AACrB,CAAC,EATW,sBAAsB,KAAtB,sBAAsB,QASjC;AAED,MAAM,OAAO,YAAY;CAMxB;AAED,MAAM,OAAO,gBAAgB;IAA7B;QAGE,YAAO,GAAY,CAAC,CAAA;QAYpB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAQnB,aAAQ,GAAY,CAAC,CAAA;QAMrB,eAAU,GAAY,CAAC,CAAA;QAEvB,eAAU,GAAY,CAAC,CAAA;IA+DzB,CAAC;IATC,aAAa;QACX,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,sBAAsB,CAAC,MAAM,CAAC;YACnC,KAAK,sBAAsB,CAAC,QAAQ;gBAClC,OAAO,IAAI,CAAA;YACb;gBACE,OAAO,KAAK,CAAA;QAChB,CAAC;IACH,CAAC;CACF","sourcesContent":["import { Domain, User, Role } from '@operato/shell'\nimport { OrgMemberTargetType, OrgMemberTarget, ApprovalLineItem } from '@things-factory/organization'\n\nimport { ActivityThread } from './activity-thread.js'\nimport { Activity, ActivityType, ActivityUIType } from './activity.js'\n\nexport enum ActivityInstanceStatus {\n Draft = 'draft',\n Issued = 'issued',\n Unassigned = 'unassigned',\n Assigned = 'assigned',\n Started = 'started',\n Pending = 'pending',\n Ended = 'ended',\n Aborted = 'aborted'\n}\n\nexport class AssigneeItem {\n type?: OrgMemberTargetType\n\n value?: string\n\n assignee?: OrgMemberTarget\n}\n\nexport class ActivityInstance {\n id?: string\n\n version?: number = 1\n\n domain?: Domain\n\n name?: string\n\n description?: string\n\n activityType?: ActivityType\n\n adhocType?: string\n\n key01?: string = ''\n\n key02?: string = ''\n\n key03?: string = ''\n\n key04?: string = ''\n\n key05?: string = ''\n\n activityThreads?: ActivityThread[]\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n priority?: number = 1\n\n state?: ActivityInstanceStatus\n\n reason?: string\n\n threadsMin?: number = 0\n\n threadsMax?: number = 0\n\n input?: { [key: string]: any }\n\n output?: { [key: string]: any }\n\n refBy?: string\n\n activity?: Activity\n\n assigneeRole?: Role\n\n supervisoryRole?: Role\n\n dueAt?: Date\n\n transaction?: string\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n createdAt?: Date\n\n updatedAt?: Date\n\n assignedAt?: Date\n\n startedAt?: Date\n\n terminatedAt?: Date\n\n creator?: User\n\n creatorId?: string\n\n updater?: User\n\n updaterId?: string\n\n issuer?: User\n\n issuerId?: string\n\n starter?: User\n\n starterId?: string\n\n terminator?: User\n\n terminatorId?: string\n\n thumbnail?: string\n\n isDelegatable(): boolean {\n switch (this.state) {\n case ActivityInstanceStatus.Issued:\n case ActivityInstanceStatus.Assigned:\n return true\n default:\n return false\n }\n }\n}\n"]}
1
+ {"version":3,"file":"activity-instance.js","sourceRoot":"","sources":["../../client/types/activity-instance.ts"],"names":[],"mappings":"AAMA,MAAM,CAAN,IAAY,sBASX;AATD,WAAY,sBAAsB;IAChC,yCAAe,CAAA;IACf,2CAAiB,CAAA;IACjB,mDAAyB,CAAA;IACzB,+CAAqB,CAAA;IACrB,6CAAmB,CAAA;IACnB,6CAAmB,CAAA;IACnB,yCAAe,CAAA;IACf,6CAAmB,CAAA;AACrB,CAAC,EATW,sBAAsB,KAAtB,sBAAsB,QASjC;AAED,MAAM,OAAO,YAAY;CAMxB;AAED,MAAM,OAAO,gBAAgB;IAA7B;QAGE,YAAO,GAAY,CAAC,CAAA;QAYpB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAEnB,UAAK,GAAY,EAAE,CAAA;QAQnB,aAAQ,GAAY,CAAC,CAAA;QAMrB,eAAU,GAAY,CAAC,CAAA;QAEvB,eAAU,GAAY,CAAC,CAAA;IA+DzB,CAAC;IATC,aAAa;QACX,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,sBAAsB,CAAC,MAAM,CAAC;YACnC,KAAK,sBAAsB,CAAC,QAAQ;gBAClC,OAAO,IAAI,CAAA;YACb;gBACE,OAAO,KAAK,CAAA;QAChB,CAAC;IACH,CAAC;CACF","sourcesContent":["import { Domain, User, Role } from '@operato/shell'\nimport { OrgMemberTargetType, OrgMemberTarget, ApprovalLineItem } from '@things-factory/organization'\n\nimport { ActivityThread } from './activity-thread.js'\nimport { Activity, ActivityType, ActivityUIType } from './activity.js'\n\nexport enum ActivityInstanceStatus {\n Draft = 'draft',\n Issued = 'issued',\n Unassigned = 'unassigned',\n Assigned = 'assigned',\n Started = 'started',\n Pending = 'pending',\n Ended = 'ended',\n Aborted = 'aborted'\n}\n\nexport class AssigneeItem {\n type?: OrgMemberTargetType\n\n value?: string\n\n assignee?: OrgMemberTarget\n}\n\nexport class ActivityInstance {\n id?: string\n\n version?: number = 1\n\n domain?: Domain\n\n name?: string\n\n description?: string\n\n activityType?: ActivityType\n\n adhocType?: string\n\n key01?: string = ''\n\n key02?: string = ''\n\n key03?: string = ''\n\n key04?: string = ''\n\n key05?: string = ''\n\n activityThreads?: ActivityThread[]\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n priority?: number = 1\n\n state?: ActivityInstanceStatus\n\n reason?: string\n\n threadsMin?: number = 0\n\n threadsMax?: number = 0\n\n input?: { [key: string]: any }\n\n output?: { [key: string]: any }\n\n refBy?: string\n\n activity?: Activity\n\n assigneeRoles?: Role[]\n\n supervisoryRole?: Role\n\n dueAt?: Date\n\n transaction?: string\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n createdAt?: Date\n\n updatedAt?: Date\n\n assignedAt?: Date\n\n startedAt?: Date\n\n terminatedAt?: Date\n\n creator?: User\n\n creatorId?: string\n\n updater?: User\n\n updaterId?: string\n\n issuer?: User\n\n issuerId?: string\n\n starter?: User\n\n starterId?: string\n\n terminator?: User\n\n terminatorId?: string\n\n thumbnail?: string\n\n isDelegatable(): boolean {\n switch (this.state) {\n case ActivityInstanceStatus.Issued:\n case ActivityInstanceStatus.Assigned:\n return true\n default:\n return false\n }\n }\n}\n"]}
@@ -49,7 +49,7 @@ export declare class Activity {
49
49
  timzeone?: string;
50
50
  standardTime?: number;
51
51
  issuerRole?: Role;
52
- assigneeRole?: Role;
52
+ assigneeRoles?: Role[];
53
53
  supervisoryRole?: Role;
54
54
  assignees?: AssigneeItem[];
55
55
  approvalLine?: ApprovalLineItem[];
@@ -1 +1 @@
1
- {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../client/types/activity.ts"],"names":[],"mappings":"AAOA,MAAM,CAAN,IAAY,cAIX;AAJD,WAAY,cAAc;IACxB,iCAAe,CAAA;IACf,uCAAqB,CAAA;IACrB,2CAAyB,CAAA;AAC3B,CAAC,EAJW,cAAc,KAAd,cAAc,QAIzB;AAED,MAAM,CAAN,IAAY,YAWX;AAXD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,iCAAiB,CAAA;IACjB,8CAA8B,CAAA;IAC9B,iCAAiB,CAAA;IACjB,0CAA0B,CAAA;IAC1B,6BAAa,CAAA;AACf,CAAC,EAXW,YAAY,KAAZ,YAAY,QAWvB;AAED,MAAM,CAAN,IAAY,cAOX;AAPD,WAAY,cAAc;IACxB,yCAAuB,CAAA;IACvB,uCAAqB,CAAA;IACrB,iCAAe,CAAA;IACf,kDAAgC,CAAA;IAChC,+BAAa,CAAA;IACb,uCAAqB,CAAA;AACvB,CAAC,EAPW,cAAc,KAAd,cAAc,QAOzB;AAED,MAAM,CAAN,IAAY,oBAGX;AAHD,WAAY,oBAAoB;IAC9B,qCAAa,CAAA;IACb,uCAAe,CAAA;AACjB,CAAC,EAHW,oBAAoB,KAApB,oBAAoB,QAG/B;AAED,MAAM,OAAO,QAAQ;IAArB;QAGE,YAAO,GAAY,CAAC,CAAA;QAkBpB,aAAQ,GAAY,CAAC,CAAA;IA2CvB,CAAC;CAAA","sourcesContent":["import { Domain, User, Role } from '@operato/shell'\nimport { ApprovalLineItem, AssigneeItem } from '@things-factory/organization/dist-client'\n\nimport { ActivityModelItem } from './activity-model-type.js'\nimport { ActivitySearchKeyItem } from './activity-search-key-item-type.js'\nimport type { DecisionKeyDeclaration } from '../components/decision-slots.js'\n\nexport enum ActivityStatus {\n Draft = 'draft',\n Released = 'released',\n Deprecated = 'deprecated'\n}\n\nexport enum ActivityType {\n Task = 'task',\n Service = 'service',\n Send = 'send',\n Receive = 'receive',\n User = 'user',\n Manual = 'manual',\n BusinessRule = 'business-rule',\n Script = 'script',\n Subprocess = 'sub-process',\n Call = 'call'\n}\n\nexport enum ActivityUIType {\n Generated = 'generated',\n Template = 'template',\n Board = 'board',\n CustomElement = 'custom-element',\n Page = 'page',\n External = 'external'\n}\n\nexport enum ActivityStartingType {\n post = 'post',\n issue = 'issue'\n}\n\nexport class Activity {\n id?: string\n\n version?: number = 1\n\n domain?: Domain\n\n name?: string\n\n description?: string\n\n activityType?: ActivityType\n\n state?: ActivityStatus\n\n searchKeys?: ActivitySearchKeyItem[]\n\n decisionKeys?: DecisionKeyDeclaration[]\n\n model?: ActivityModelItem[]\n\n priority?: number = 1\n\n startingType?: ActivityStartingType\n\n schedule?: string\n\n timzeone?: string\n\n standardTime?: number\n\n issuerRole?: Role\n\n assigneeRole?: Role\n\n supervisoryRole?: Role\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n viewType?: ActivityUIType\n\n viewSource?: string\n\n reportType?: ActivityUIType\n\n reportSource?: string\n\n createdAt?: Date\n\n updatedAt?: Date\n\n client?: User\n\n creator?: User\n\n updater?: User\n\n thumbnail?: string\n}\n"]}
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../client/types/activity.ts"],"names":[],"mappings":"AAOA,MAAM,CAAN,IAAY,cAIX;AAJD,WAAY,cAAc;IACxB,iCAAe,CAAA;IACf,uCAAqB,CAAA;IACrB,2CAAyB,CAAA;AAC3B,CAAC,EAJW,cAAc,KAAd,cAAc,QAIzB;AAED,MAAM,CAAN,IAAY,YAWX;AAXD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,iCAAiB,CAAA;IACjB,8CAA8B,CAAA;IAC9B,iCAAiB,CAAA;IACjB,0CAA0B,CAAA;IAC1B,6BAAa,CAAA;AACf,CAAC,EAXW,YAAY,KAAZ,YAAY,QAWvB;AAED,MAAM,CAAN,IAAY,cAOX;AAPD,WAAY,cAAc;IACxB,yCAAuB,CAAA;IACvB,uCAAqB,CAAA;IACrB,iCAAe,CAAA;IACf,kDAAgC,CAAA;IAChC,+BAAa,CAAA;IACb,uCAAqB,CAAA;AACvB,CAAC,EAPW,cAAc,KAAd,cAAc,QAOzB;AAED,MAAM,CAAN,IAAY,oBAGX;AAHD,WAAY,oBAAoB;IAC9B,qCAAa,CAAA;IACb,uCAAe,CAAA;AACjB,CAAC,EAHW,oBAAoB,KAApB,oBAAoB,QAG/B;AAED,MAAM,OAAO,QAAQ;IAArB;QAGE,YAAO,GAAY,CAAC,CAAA;QAkBpB,aAAQ,GAAY,CAAC,CAAA;IA2CvB,CAAC;CAAA","sourcesContent":["import { Domain, User, Role } from '@operato/shell'\nimport { ApprovalLineItem, AssigneeItem } from '@things-factory/organization/dist-client'\n\nimport { ActivityModelItem } from './activity-model-type.js'\nimport { ActivitySearchKeyItem } from './activity-search-key-item-type.js'\nimport type { DecisionKeyDeclaration } from '../components/decision-slots.js'\n\nexport enum ActivityStatus {\n Draft = 'draft',\n Released = 'released',\n Deprecated = 'deprecated'\n}\n\nexport enum ActivityType {\n Task = 'task',\n Service = 'service',\n Send = 'send',\n Receive = 'receive',\n User = 'user',\n Manual = 'manual',\n BusinessRule = 'business-rule',\n Script = 'script',\n Subprocess = 'sub-process',\n Call = 'call'\n}\n\nexport enum ActivityUIType {\n Generated = 'generated',\n Template = 'template',\n Board = 'board',\n CustomElement = 'custom-element',\n Page = 'page',\n External = 'external'\n}\n\nexport enum ActivityStartingType {\n post = 'post',\n issue = 'issue'\n}\n\nexport class Activity {\n id?: string\n\n version?: number = 1\n\n domain?: Domain\n\n name?: string\n\n description?: string\n\n activityType?: ActivityType\n\n state?: ActivityStatus\n\n searchKeys?: ActivitySearchKeyItem[]\n\n decisionKeys?: DecisionKeyDeclaration[]\n\n model?: ActivityModelItem[]\n\n priority?: number = 1\n\n startingType?: ActivityStartingType\n\n schedule?: string\n\n timzeone?: string\n\n standardTime?: number\n\n issuerRole?: Role\n\n assigneeRoles?: Role[]\n\n supervisoryRole?: Role\n\n assignees?: AssigneeItem[]\n\n approvalLine?: ApprovalLineItem[]\n\n uiType?: ActivityUIType\n\n uiSource?: string\n\n viewType?: ActivityUIType\n\n viewSource?: string\n\n reportType?: ActivityUIType\n\n reportSource?: string\n\n createdAt?: Date\n\n updatedAt?: Date\n\n client?: User\n\n creator?: User\n\n updater?: User\n\n thumbnail?: string\n}\n"]}
@@ -11,8 +11,8 @@ import type { JudgeableSeat } from './own-request.js';
11
11
  * 앉히는** 일이 난다. 그쪽이 가르는 축은 하나다 — `type` 이 `Role` 이면 역할로 걸고, 아니면 사람
12
12
  * 하나로 푼다.
13
13
  *
14
- * ⚠ `getSystemUserFromOrgMemberItem` 도 `Role` 을 받아 주는데, 그것은 **그 역할의 첫 사람 하나**를
15
- * 돌려준다. 실제 거동은 그게 아니라 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.
14
+ * ⚠ `getSystemUserFromOrgMemberItem` 은 `Role` 을 사람으로 풀지 않고 거절한다(10-20 전까지는 **그 역할의
15
+ * 첫 사람 하나**를 돌려줬다). 실제 거동은 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.
16
16
  *
17
17
  * ── `Myself` 는 올리는 사람이다 ───────────────────────────────────────────
18
18
  * 그 자리는 `context.state.user` 로 풀리고, 상신하는 동안 그 사람이 곧 상신자다. 그래서 `Myself` 가
@@ -17,8 +17,8 @@ const approval_line_standing_js_1 = require("../../service/work-door/approval-li
17
17
  * 앉히는** 일이 난다. 그쪽이 가르는 축은 하나다 — `type` 이 `Role` 이면 역할로 걸고, 아니면 사람
18
18
  * 하나로 푼다.
19
19
  *
20
- * ⚠ `getSystemUserFromOrgMemberItem` 도 `Role` 을 받아 주는데, 그것은 **그 역할의 첫 사람 하나**를
21
- * 돌려준다. 실제 거동은 그게 아니라 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.
20
+ * ⚠ `getSystemUserFromOrgMemberItem` 은 `Role` 을 사람으로 풀지 않고 거절한다(10-20 전까지는 **그 역할의
21
+ * 첫 사람 하나**를 돌려줬다). 실제 거동은 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.
22
22
  *
23
23
  * ── `Myself` 는 올리는 사람이다 ───────────────────────────────────────────
24
24
  * 그 자리는 `context.state.user` 로 풀리고, 상신하는 동안 그 사람이 곧 상신자다. 그래서 `Myself` 가
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-judgeable-seats.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/resolve-judgeable-seats.ts"],"names":[],"mappings":";;AA2BA,sDA2CC;AAtED,yDAAgD;AAEhD,+DAAkE;AAElE,4CAA6D;AAC7D,yDAAmD;AAEnD,iGAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,qBAAqB,CACzC,YAAmD,EACnD,OAAwB;IAExB,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,MAAM,KAAK,GAAoB,EAAE,CAAA;IAEjC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,yCAAyC;QACzC,IAAI,CAAC,IAAA,iCAAc,EAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,SAAQ;QACV,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAA;QAEvB,IAAI,IAAI,CAAC,IAAI,IAAI,kCAAmB,CAAC,IAAI,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAA,sCAAU,EAAC,IAAI,CAAC,CAAA;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,gBAAI,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YAE9G,KAAK,CAAC,IAAI,CAAC;gBACT,KAAK;gBACL,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClD,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI;gBACvC,QAAQ,EAAE,MAAM,IAAI,SAAS;aAC9B,CAAC,CAAA;YAEF,SAAQ;QACV,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,0CAA8B,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEpE,KAAK,CAAC,IAAI,CAAC;YACT,KAAK;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;YAC7C,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI;YAC3C,qDAAqD;YACrD,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,IAAI,CAAC,KAAK,IAAI,SAAS;SACvD,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["import { Role } from '@things-factory/auth-base'\nimport type { ApprovalLineItem } from '@things-factory/organization'\nimport { OrgMemberTargetType } from '@things-factory/organization'\n\nimport { getSystemUserFromOrgMemberItem } from '../common.js'\nimport { isApprovalStep } from './approval-line.js'\nimport type { JudgeableSeat } from './own-request.js'\nimport { seatRoleId } from '../../service/work-door/approval-line-standing.js'\n\n/**\n * 결재선을 「자리마다 누가 설 수 있나」로 푼다 — **가져오는 일만 한다.**\n *\n * 판단은 `own-request.ts` 의 `judgeSomeoneElseCanJudge` 가 든다. 이 파일은 DB 를 읽어서 시험이 못\n * 드는 쪽이고, 그래서 여기에는 규칙을 한 줄도 두지 않는다.\n *\n * ── 자리를 푸는 규칙은 `create-approval.ts` 를 그대로 따른다 ──────────────\n * 실제로 행을 만드는 자리가 거기다. 여기서 다르게 풀면 **여기서는 괜찮다고 하고 거기서는 다른 사람을\n * 앉히는** 일이 난다. 그쪽이 가르는 축은 하나다 — `type` 이 `Role` 이면 역할로 걸고, 아니면 사람\n * 하나로 푼다.\n *\n * ⚠ `getSystemUserFromOrgMemberItem` 도 `Role` 을 받아 주는데, 그것은 **그 역할의 첫 사람 하나**를\n * 돌려준다. 실제 거동은 그게 아니라 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.\n *\n * ── `Myself` 는 올리는 사람이다 ───────────────────────────────────────────\n * 그 자리는 `context.state.user` 로 풀리고, 상신하는 동안 그 사람이 곧 상신자다. 그래서 `Myself` 가\n * 결재 자리에 있으면 **언제나** 자기결재다 — 이 문이 정확히 그것을 잡는다.\n */\nexport async function resolveJudgeableSeats(\n approvalLine: ApprovalLineItem[] | undefined | null,\n context: ResolverContext\n): Promise<JudgeableSeat[]> {\n const { tx } = context.state\n const seats: JudgeableSeat[] = []\n\n for (const [index, item] of (approvalLine || []).entries()) {\n /* 참조는 판정하지 않는다. 자리는 차지하므로 차례는 그대로 센다. */\n if (!isApprovalStep(item)) {\n continue\n }\n\n const order = index + 1\n\n if (item.type == OrgMemberTargetType.Role) {\n const roleId = seatRoleId(item)\n const role = roleId && (await tx.getRepository(Role).findOne({ where: { id: roleId }, relations: ['users'] }))\n\n seats.push({\n order,\n kind: 'role',\n couldJudge: (role?.users || []).map(one => one.id),\n name: role?.name || item.approver?.name,\n pointsAt: roleId || undefined\n })\n\n continue\n }\n\n const approver = await getSystemUserFromOrgMemberItem(item, context)\n\n seats.push({\n order,\n kind: 'person',\n couldJudge: approver?.id ? [approver.id] : [],\n name: approver?.name || item.approver?.name,\n /* 안 풀리는 자리는 이름도 비므로, 결재선에 적힌 값이 관리자가 가진 유일한 실마리다. */\n pointsAt: item.approver?.id || item.value || undefined\n })\n }\n\n return seats\n}\n"]}
1
+ {"version":3,"file":"resolve-judgeable-seats.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/resolve-judgeable-seats.ts"],"names":[],"mappings":";;AA2BA,sDA2CC;AAtED,yDAAgD;AAEhD,+DAAkE;AAElE,4CAA6D;AAC7D,yDAAmD;AAEnD,iGAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,qBAAqB,CACzC,YAAmD,EACnD,OAAwB;IAExB,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,MAAM,KAAK,GAAoB,EAAE,CAAA;IAEjC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,yCAAyC;QACzC,IAAI,CAAC,IAAA,iCAAc,EAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,SAAQ;QACV,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAA;QAEvB,IAAI,IAAI,CAAC,IAAI,IAAI,kCAAmB,CAAC,IAAI,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAA,sCAAU,EAAC,IAAI,CAAC,CAAA;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,gBAAI,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YAE9G,KAAK,CAAC,IAAI,CAAC;gBACT,KAAK;gBACL,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClD,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI;gBACvC,QAAQ,EAAE,MAAM,IAAI,SAAS;aAC9B,CAAC,CAAA;YAEF,SAAQ;QACV,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,0CAA8B,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEpE,KAAK,CAAC,IAAI,CAAC;YACT,KAAK;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;YAC7C,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI;YAC3C,qDAAqD;YACrD,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,IAAI,CAAC,KAAK,IAAI,SAAS;SACvD,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["import { Role } from '@things-factory/auth-base'\nimport type { ApprovalLineItem } from '@things-factory/organization'\nimport { OrgMemberTargetType } from '@things-factory/organization'\n\nimport { getSystemUserFromOrgMemberItem } from '../common.js'\nimport { isApprovalStep } from './approval-line.js'\nimport type { JudgeableSeat } from './own-request.js'\nimport { seatRoleId } from '../../service/work-door/approval-line-standing.js'\n\n/**\n * 결재선을 「자리마다 누가 설 수 있나」로 푼다 — **가져오는 일만 한다.**\n *\n * 판단은 `own-request.ts` 의 `judgeSomeoneElseCanJudge` 가 든다. 이 파일은 DB 를 읽어서 시험이 못\n * 드는 쪽이고, 그래서 여기에는 규칙을 한 줄도 두지 않는다.\n *\n * ── 자리를 푸는 규칙은 `create-approval.ts` 를 그대로 따른다 ──────────────\n * 실제로 행을 만드는 자리가 거기다. 여기서 다르게 풀면 **여기서는 괜찮다고 하고 거기서는 다른 사람을\n * 앉히는** 일이 난다. 그쪽이 가르는 축은 하나다 — `type` 이 `Role` 이면 역할로 걸고, 아니면 사람\n * 하나로 푼다.\n *\n * ⚠ `getSystemUserFromOrgMemberItem` 은 `Role` 을 사람으로 풀지 않고 거절한다(10-20 전까지는 **그 역할의\n * 첫 사람 하나**를 돌려줬다). 실제 거동은 「그 역할을 가진 사람 누구나」이므로 그 갈래를 먼저 가른다.\n *\n * ── `Myself` 는 올리는 사람이다 ───────────────────────────────────────────\n * 그 자리는 `context.state.user` 로 풀리고, 상신하는 동안 그 사람이 곧 상신자다. 그래서 `Myself` 가\n * 결재 자리에 있으면 **언제나** 자기결재다 — 이 문이 정확히 그것을 잡는다.\n */\nexport async function resolveJudgeableSeats(\n approvalLine: ApprovalLineItem[] | undefined | null,\n context: ResolverContext\n): Promise<JudgeableSeat[]> {\n const { tx } = context.state\n const seats: JudgeableSeat[] = []\n\n for (const [index, item] of (approvalLine || []).entries()) {\n /* 참조는 판정하지 않는다. 자리는 차지하므로 차례는 그대로 센다. */\n if (!isApprovalStep(item)) {\n continue\n }\n\n const order = index + 1\n\n if (item.type == OrgMemberTargetType.Role) {\n const roleId = seatRoleId(item)\n const role = roleId && (await tx.getRepository(Role).findOne({ where: { id: roleId }, relations: ['users'] }))\n\n seats.push({\n order,\n kind: 'role',\n couldJudge: (role?.users || []).map(one => one.id),\n name: role?.name || item.approver?.name,\n pointsAt: roleId || undefined\n })\n\n continue\n }\n\n const approver = await getSystemUserFromOrgMemberItem(item, context)\n\n seats.push({\n order,\n kind: 'person',\n couldJudge: approver?.id ? [approver.id] : [],\n name: approver?.name || item.approver?.name,\n /* 안 풀리는 자리는 이름도 비므로, 결재선에 적힌 값이 관리자가 가진 유일한 실마리다. */\n pointsAt: item.approver?.id || item.value || undefined\n })\n }\n\n return seats\n}\n"]}
@@ -14,7 +14,7 @@ async function abort({ id, reason }, context) {
14
14
  'domain',
15
15
  'activity',
16
16
  'activityThreads',
17
- 'assigneeRole',
17
+ 'assigneeRoles',
18
18
  'supervisoryRole',
19
19
  'updater',
20
20
  'creator',
@@ -1 +1 @@
1
- {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/abort.ts"],"names":[],"mappings":";;AAMA,sBAuEC;AA7ED,yFAAuF;AACvF,+FAA+G;AAC/G,4CAAwD;AAExD,4DAA4E;AAErE,KAAK,UAAU,KAAK,CACzB,EAAE,EAAE,EAAE,MAAM,EAAkC,EAC9C,OAAwB;IAExB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE1C,IAAI,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAA;IAEnD,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;YACT,QAAQ;YACR,UAAU;YACV,iBAAiB;YACjB,cAAc;YACd,iBAAiB;YACjB,SAAS;YACT,SAAS;YACT,SAAS;YACT,YAAY;SACb;KACF,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,IAAI,6CAAsB,CAAC,OAAO;QACxD,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,KAAK,EACtD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,+CAA+C,EAAE;YACzD,EAAE;YACF,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED;;;MAGE;IAEF,MAAM,UAAU,GAAG,CAAC,yCAAoB,CAAC,OAAO,EAAE,yCAAoB,CAAC,KAAK,CAAC,CAAA;IAE7E,KAAK,IAAI,cAAc,IAAI,gBAAgB,CAAC,eAAe,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,SAAQ;QACV,CAAC;QAED,MAAM,IAAA,kBAAmB,EAAC,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAA;IAChE,CAAC;IAED,OAAO,MAAM,UAAU,CAAC,IAAI,CAAC;QAC3B,GAAG,gBAAgB;QACnB,MAAM;QACN,GAAG,CAAC,MAAM,IAAA,qCAAyB,EAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjD,WAAW,EAAE,OAAO;QACpB,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { evalActivityInstanceState } from '../common.js'\n\nimport { _abort as abortActivityThread } from '../activity-thread/_abort.js'\n\nexport async function abort(\n { id, reason }: { id: string; reason: string },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n\n var repository = tx.getRepository(ActivityInstance)\n\n var activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: [\n 'domain',\n 'activity',\n 'activityThreads',\n 'assigneeRole',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\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 Aborted.\n - The previous state of the task must not be End or Aborted.\n */\n if (\n activityInstance.state == ActivityInstanceStatus.Aborted ||\n activityInstance.state == ActivityInstanceStatus.Ended\n ) {\n throw new Error(\n context.t(`error.activity-instance is already terminated`, {\n id,\n state: activityInstance.state\n })\n )\n }\n\n /* \n Actions to be processed when a task is aborted\n - All related activity threads must be aborted.\n */\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n\n for (let activityThread of activityInstance.activityThreads) {\n if (TERMINATED.includes(activityThread.state)) {\n continue\n }\n\n await abortActivityThread({ activityThread, reason }, context)\n }\n\n return await repository.save({\n ...activityInstance,\n reason,\n ...(await evalActivityInstanceState(id, context)),\n transaction: 'abort',\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n}\n"]}
1
+ {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/abort.ts"],"names":[],"mappings":";;AAMA,sBAuEC;AA7ED,yFAAuF;AACvF,+FAA+G;AAC/G,4CAAwD;AAExD,4DAA4E;AAErE,KAAK,UAAU,KAAK,CACzB,EAAE,EAAE,EAAE,MAAM,EAAkC,EAC9C,OAAwB;IAExB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE1C,IAAI,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAA;IAEnD,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;YACT,QAAQ;YACR,UAAU;YACV,iBAAiB;YACjB,eAAe;YACf,iBAAiB;YACjB,SAAS;YACT,SAAS;YACT,SAAS;YACT,YAAY;SACb;KACF,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,IAAI,6CAAsB,CAAC,OAAO;QACxD,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,KAAK,EACtD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,+CAA+C,EAAE;YACzD,EAAE;YACF,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED;;;MAGE;IAEF,MAAM,UAAU,GAAG,CAAC,yCAAoB,CAAC,OAAO,EAAE,yCAAoB,CAAC,KAAK,CAAC,CAAA;IAE7E,KAAK,IAAI,cAAc,IAAI,gBAAgB,CAAC,eAAe,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,SAAQ;QACV,CAAC;QAED,MAAM,IAAA,kBAAmB,EAAC,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAA;IAChE,CAAC;IAED,OAAO,MAAM,UAAU,CAAC,IAAI,CAAC;QAC3B,GAAG,gBAAgB;QACnB,MAAM;QACN,GAAG,CAAC,MAAM,IAAA,qCAAyB,EAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjD,WAAW,EAAE,OAAO;QACpB,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { evalActivityInstanceState } from '../common.js'\n\nimport { _abort as abortActivityThread } from '../activity-thread/_abort.js'\n\nexport async function abort(\n { id, reason }: { id: string; reason: string },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n\n var repository = tx.getRepository(ActivityInstance)\n\n var activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: [\n 'domain',\n 'activity',\n 'activityThreads',\n 'assigneeRoles',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\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 Aborted.\n - The previous state of the task must not be End or Aborted.\n */\n if (\n activityInstance.state == ActivityInstanceStatus.Aborted ||\n activityInstance.state == ActivityInstanceStatus.Ended\n ) {\n throw new Error(\n context.t(`error.activity-instance is already terminated`, {\n id,\n state: activityInstance.state\n })\n )\n }\n\n /* \n Actions to be processed when a task is aborted\n - All related activity threads must be aborted.\n */\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n\n for (let activityThread of activityInstance.activityThreads) {\n if (TERMINATED.includes(activityThread.state)) {\n continue\n }\n\n await abortActivityThread({ activityThread, reason }, context)\n }\n\n return await repository.save({\n ...activityInstance,\n reason,\n ...(await evalActivityInstanceState(id, context)),\n transaction: 'abort',\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  import { ActivityInstance } from '../../service/activity-instance/activity-instance.js';
2
- export declare function assign({ id, assignees, reason, dueAt }: {
2
+ export declare function assign({ id, assignees, assigneeRoles, reason, dueAt }: {
3
3
  id: string;
4
4
  assignees?: string[];
5
+ assigneeRoles?: string[];
5
6
  reason?: string;
6
7
  dueAt?: Date;
7
8
  }, context: ResolverContext): Promise<ActivityInstance>;
@@ -3,20 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assign = assign;
4
4
  const typeorm_1 = require("typeorm");
5
5
  const auth_base_1 = require("@things-factory/auth-base");
6
+ const shell_1 = require("@things-factory/shell");
6
7
  const activity_instance_js_1 = require("../../service/activity-instance/activity-instance.js");
8
+ const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
9
+ const assignee_pool_js_1 = require("../../service/activity-instance/assignee-pool.js");
7
10
  const common_js_1 = require("../common.js");
8
- async function assign({ id, assignees, reason, dueAt }, context) {
11
+ async function assign({ id, assignees, assigneeRoles, reason, dueAt }, context) {
9
12
  const { domain, user, tx } = context.state;
10
13
  const repository = tx.getRepository(activity_instance_js_1.ActivityInstance);
11
14
  var activityInstance = await repository.findOne({
12
15
  where: { domain: { id: domain.id }, id },
13
- relations: ['domain', 'activity', 'assigneeRole', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']
16
+ relations: ['domain', 'activity', 'assigneeRoles', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']
14
17
  });
15
18
  if (!activityInstance) {
16
19
  throw new Error(context.t('error.activity-instance not found', {
17
20
  activityInstance: id
18
21
  }));
19
22
  }
23
+ /*
24
+ * People or a pool, not both in one call. Given both, the case would wait for the pool and already be
25
+ * held by the people, and which of the two it means could not be read off the case afterwards.
26
+ */
27
+ if (assigneeRoles?.length && assignees?.length) {
28
+ throw new Error(context.t('error.activity-instance assign people or a role, not both', { id }));
29
+ }
30
+ if (assigneeRoles?.length) {
31
+ return await assignToPool(activityInstance, assigneeRoles, dueAt, context);
32
+ }
20
33
  /*
21
34
  Prerequisites for a Task to Be Assigned.
22
35
  - The previous state of the task must not be Assigned.
@@ -47,4 +60,93 @@ async function assign({ id, assignees, reason, dueAt }, context) {
47
60
  await (0, common_js_1.createActivityThreadsForUsers)('assign', activityInstance, assignedUsers, context);
48
61
  return await tx.getRepository(activity_instance_js_1.ActivityInstance).findOneBy({ id: activityInstance.id });
49
62
  }
63
+ /**
64
+ * Put a case in a pool: whoever holds any one of its roles and picks it first holds it.
65
+ *
66
+ * The pool is what issue() makes when the activity names assignee roles and waits for one person -
67
+ * `assigneeRoles` on the case, `threadsMin` 1, and one unassigned thread. To-do lists and the pickable
68
+ * list read exactly that (assignee-pool.ts), so this makes the same shape and nothing else; a second
69
+ * shape would be a second pool nobody's list reads.
70
+ *
71
+ * Only a case nobody holds - `Issued`, no valid thread (common.ts evalActivityInstanceState). A case
72
+ * someone already holds is theirs; taking it back into a pool is another action, not this one.
73
+ */
74
+ async function assignToPool(activityInstance, roleIds, dueAt, context) {
75
+ const { domain, user, tx } = context.state;
76
+ const repository = tx.getRepository(activity_instance_js_1.ActivityInstance);
77
+ const { id } = activityInstance;
78
+ if (activityInstance.state !== activity_instance_js_1.ActivityInstanceStatus.Issued) {
79
+ throw new Error(context.t('error.activity-instance should not be assigned', {
80
+ id,
81
+ actual: activityInstance.state
82
+ }));
83
+ }
84
+ /*
85
+ * Roles of this domain only, each once - the same reason as people above: another tenant's pool is read
86
+ * by nobody here (assignee-pool.ts refuses by name).
87
+ */
88
+ const roles = await (0, assignee_pool_js_1.resolvePoolRoles)(roleIds, context);
89
+ /*
90
+ * A pool nobody is in is a case nobody sees. Its people are every holder of any of its roles, counted
91
+ * once each, the way issue() counts them (createActivityThreadsForAllRoleUsers).
92
+ */
93
+ if (!(0, assignee_pool_js_1.poolMembers)(roles).length) {
94
+ throw new Error(context.t('error.activity-instance role has no members', { role: roles.map(role => role.name).join(', ') }));
95
+ }
96
+ /*
97
+ * Take hold of the case first, as pick does: the UPDATE matches only a case still issued and holds its
98
+ * row to the end of this transaction. Every way into a waiting case takes this row before its threads -
99
+ * pick's UPDATE, and a member claiming the pool's waiting thread (activity-thread/check-authority.ts) - so
100
+ * whoever is second waits for the first to commit and then reads what it did. One order everywhere, so
101
+ * no two of them deadlock.
102
+ */
103
+ const claimed = await repository.update({ domain: { id: domain.id }, id, state: activity_instance_js_1.ActivityInstanceStatus.Issued }, { threadsMin: 1, updater: user, ...(dueAt ? { dueAt } : {}) });
104
+ if (!claimed.affected) {
105
+ throw new Error(context.t('error.activity-approval taken by someone else', { activityApproval: id }));
106
+ }
107
+ /*
108
+ * Nobody holds it - asked of the threads too, not only of the state. The state is worked out again only
109
+ * when a thread changes (common.ts updateActivityInstanceState); a thread someone holds under a case still
110
+ * marked issued is theirs, and the refusal rolls the UPDATE above back.
111
+ */
112
+ const threads = await tx.getRepository(activity_thread_js_1.ActivityThread).find({
113
+ where: { activityInstance: { id } },
114
+ /* A locking read is a current read - a plain one may answer from the snapshot taken before the wait. */
115
+ ...((0, shell_1.writeLockSupported)(tx) ? { lock: { mode: 'pessimistic_write' } } : {})
116
+ });
117
+ if (threads.some(thread => thread.isValid())) {
118
+ throw new Error(context.t('error.activity-approval taken by someone else', { activityApproval: id }));
119
+ }
120
+ /*
121
+ * The pool itself is a join table, not a column the UPDATE can set. Written under the hold the UPDATE took,
122
+ * and written whole: every row of this case goes, then the new list. Worked out as a difference from the
123
+ * case read above, it was a difference from a read taken before the hold - two assigns at once on postgres
124
+ * left the union of both lists, the pool of neither (measured 2026-10-20). A DELETE reads what is there
125
+ * now, so the pool is the list of whichever assign held the case last. Not a save - that would write a
126
+ * history row of a half-read case.
127
+ */
128
+ await tx
129
+ .createQueryBuilder()
130
+ .delete()
131
+ .from(assignee_pool_js_1.CASE_POOL.table)
132
+ .where(`${assignee_pool_js_1.CASE_POOL.caseColumn} = :id`, { id })
133
+ .execute();
134
+ await repository
135
+ .createQueryBuilder()
136
+ .relation(activity_instance_js_1.ActivityInstance, 'assigneeRoles')
137
+ .of(id)
138
+ .add(roles.map(role => role.id));
139
+ const pooled = await repository.findOne({
140
+ where: { id },
141
+ relations: ['domain', 'activity', 'assigneeRoles', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']
142
+ });
143
+ /*
144
+ * A case already in a pool moves to the other pool with its one waiting thread - nobody has taken it, so
145
+ * a manager who chose the wrong role puts it right here instead of aborting the case.
146
+ */
147
+ if (!threads.some(thread => thread.state === activity_thread_js_1.ActivityThreadStatus.Unassigned)) {
148
+ await (0, common_js_1.createActivityThreadWithUnassigned)('assign', pooled, context);
149
+ }
150
+ return await repository.findOneBy({ id });
151
+ }
50
152
  //# sourceMappingURL=assign.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assign.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/assign.ts"],"names":[],"mappings":";;AAOA,wBA4DC;AAnED,qCAA4B;AAE5B,yDAAgD;AAEhD,+FAA+G;AAC/G,4CAA4D;AAErD,KAAK,UAAU,MAAM,CAC1B,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAuE,EACrG,OAAwB;IAExB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE1C,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,IAAI,KAAK,EAAE,CAAC;QACV,gBAAgB,CAAC,KAAK,GAAG,KAAK,CAAA;IAChC,CAAC;IAED;;;;OAIG;IACH,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,gBAAI,CAAC,CAAC,IAAI,CAAC;QACtD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,YAAE,EAAC,SAAS,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;QACjE,SAAS,EAAE,CAAC,SAAS,CAAC;KACvB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAA;IAElG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,qDAAqD,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IACnH,CAAC;IAED,MAAM,IAAA,yCAA6B,EAAC,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA;IAEvF,OAAO,MAAM,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAA;AACxF,CAAC","sourcesContent":["import { In } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\n\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { createActivityThreadsForUsers } from '../common.js'\n\nexport async function assign(\n { id, assignees, reason, dueAt }: { id: string; assignees?: string[]; reason?: string; dueAt?: Date },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n\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 Assigned.\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 if (dueAt) {\n activityInstance.dueAt = dueAt\n }\n\n /*\n * People of this domain only. By email alone a manager could hand the task to someone of another\n * tenant, whose to-do list reads their own domain and would never show it. An address that is not\n * here is named rather than dropped - dropped, the task went to fewer people than were asked.\n */\n const assignedUsers = await tx.getRepository(User).find({\n where: { email: In(assignees || []), domains: { id: domain.id } },\n relations: ['domains']\n })\n\n const missing = (assignees || []).filter(email => !assignedUsers.some(one => one.email === email))\n\n if (missing.length) {\n throw new Error(context.t('error.activity-instance assignee not in this domain', { emails: missing.join(', ') }))\n }\n\n await createActivityThreadsForUsers('assign', activityInstance, assignedUsers, context)\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: activityInstance.id })\n}\n"]}
1
+ {"version":3,"file":"assign.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/assign.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { writeLockSupported } from '@things-factory/shell'\n\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThread, ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { CASE_POOL, poolMembers, resolvePoolRoles } from '../../service/activity-instance/assignee-pool.js'\nimport { createActivityThreadWithUnassigned, createActivityThreadsForUsers } from '../common.js'\n\nexport async function assign(\n {\n id,\n assignees,\n assigneeRoles,\n reason,\n dueAt\n }: { id: string; assignees?: string[]; assigneeRoles?: string[]; reason?: string; dueAt?: Date },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n\n const repository = tx.getRepository(ActivityInstance)\n\n var activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activity', 'assigneeRoles', '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 * People or a pool, not both in one call. Given both, the case would wait for the pool and already be\n * held by the people, and which of the two it means could not be read off the case afterwards.\n */\n if (assigneeRoles?.length && assignees?.length) {\n throw new Error(context.t('error.activity-instance assign people or a role, not both', { id }))\n }\n\n if (assigneeRoles?.length) {\n return await assignToPool(activityInstance, assigneeRoles, dueAt, context)\n }\n\n /*\n Prerequisites for a Task to Be Assigned.\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 if (dueAt) {\n activityInstance.dueAt = dueAt\n }\n\n /*\n * People of this domain only. By email alone a manager could hand the task to someone of another\n * tenant, whose to-do list reads their own domain and would never show it. An address that is not\n * here is named rather than dropped - dropped, the task went to fewer people than were asked.\n */\n const assignedUsers = await tx.getRepository(User).find({\n where: { email: In(assignees || []), domains: { id: domain.id } },\n relations: ['domains']\n })\n\n const missing = (assignees || []).filter(email => !assignedUsers.some(one => one.email === email))\n\n if (missing.length) {\n throw new Error(context.t('error.activity-instance assignee not in this domain', { emails: missing.join(', ') }))\n }\n\n await createActivityThreadsForUsers('assign', activityInstance, assignedUsers, context)\n\n return await tx.getRepository(ActivityInstance).findOneBy({ id: activityInstance.id })\n}\n\n/**\n * Put a case in a pool: whoever holds any one of its roles and picks it first holds it.\n *\n * The pool is what issue() makes when the activity names assignee roles and waits for one person -\n * `assigneeRoles` on the case, `threadsMin` 1, and one unassigned thread. To-do lists and the pickable\n * list read exactly that (assignee-pool.ts), so this makes the same shape and nothing else; a second\n * shape would be a second pool nobody's list reads.\n *\n * Only a case nobody holds - `Issued`, no valid thread (common.ts evalActivityInstanceState). A case\n * someone already holds is theirs; taking it back into a pool is another action, not this one.\n */\nasync function assignToPool(\n activityInstance: ActivityInstance,\n roleIds: string[],\n dueAt: Date | undefined,\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(ActivityInstance)\n const { id } = activityInstance\n\n if (activityInstance.state !== ActivityInstanceStatus.Issued) {\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 /*\n * Roles of this domain only, each once - the same reason as people above: another tenant's pool is read\n * by nobody here (assignee-pool.ts refuses by name).\n */\n const roles = await resolvePoolRoles(roleIds, context)\n\n /*\n * A pool nobody is in is a case nobody sees. Its people are every holder of any of its roles, counted\n * once each, the way issue() counts them (createActivityThreadsForAllRoleUsers).\n */\n if (!poolMembers(roles).length) {\n throw new Error(context.t('error.activity-instance role has no members', { role: roles.map(role => role.name).join(', ') }))\n }\n\n /*\n * Take hold of the case first, as pick does: the UPDATE matches only a case still issued and holds its\n * row to the end of this transaction. Every way into a waiting case takes this row before its threads -\n * pick's UPDATE, and a member claiming the pool's waiting thread (activity-thread/check-authority.ts) - so\n * whoever is second waits for the first to commit and then reads what it did. One order everywhere, so\n * no two of them deadlock.\n */\n const claimed = await repository.update(\n { domain: { id: domain.id }, id, state: ActivityInstanceStatus.Issued },\n { threadsMin: 1, updater: user, ...(dueAt ? { dueAt } : {}) }\n )\n\n if (!claimed.affected) {\n throw new Error(context.t('error.activity-approval taken by someone else', { activityApproval: id }))\n }\n\n /*\n * Nobody holds it - asked of the threads too, not only of the state. The state is worked out again only\n * when a thread changes (common.ts updateActivityInstanceState); a thread someone holds under a case still\n * marked issued is theirs, and the refusal rolls the UPDATE above back.\n */\n const threads = await tx.getRepository(ActivityThread).find({\n where: { activityInstance: { id } },\n /* A locking read is a current read - a plain one may answer from the snapshot taken before the wait. */\n ...(writeLockSupported(tx) ? { lock: { mode: 'pessimistic_write' as const } } : {})\n })\n\n if (threads.some(thread => thread.isValid())) {\n throw new Error(context.t('error.activity-approval taken by someone else', { activityApproval: id }))\n }\n\n /*\n * The pool itself is a join table, not a column the UPDATE can set. Written under the hold the UPDATE took,\n * and written whole: every row of this case goes, then the new list. Worked out as a difference from the\n * case read above, it was a difference from a read taken before the hold - two assigns at once on postgres\n * left the union of both lists, the pool of neither (measured 2026-10-20). A DELETE reads what is there\n * now, so the pool is the list of whichever assign held the case last. Not a save - that would write a\n * history row of a half-read case.\n */\n await tx\n .createQueryBuilder()\n .delete()\n .from(CASE_POOL.table)\n .where(`${CASE_POOL.caseColumn} = :id`, { id })\n .execute()\n\n await repository\n .createQueryBuilder()\n .relation(ActivityInstance, 'assigneeRoles')\n .of(id)\n .add(roles.map(role => role.id))\n\n const pooled = await repository.findOne({\n where: { id },\n relations: ['domain', 'activity', 'assigneeRoles', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']\n })\n\n /*\n * A case already in a pool moves to the other pool with its one waiting thread - nobody has taken it, so\n * a manager who chose the wrong role puts it right here instead of aborting the case.\n */\n if (!threads.some(thread => thread.state === ActivityThreadStatus.Unassigned)) {\n await createActivityThreadWithUnassigned('assign', pooled, context)\n }\n\n return await repository.findOneBy({ id })\n}\n"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A role among the assignees of an activity or a case (ruling 2026-10-20).
3
+ *
4
+ * An assignee entry names one person. A role is many, and the entry made it one: issue() turned each
5
+ * entry into a person through getSystemUserFromOrgMemberItem, whose role branch answered the role's
6
+ * first user - an unordered relation, so whoever the database listed first got the work and the rest
7
+ * of the role never saw it.
8
+ *
9
+ * "Anyone of this role" already has its one place: `assigneeRoles`, on the activity and on the case.
10
+ * That is what the to-do lists and the pickable list read (my-open-threads.ts). Reading a role entry as
11
+ * that pool would make the pool live in two places, and would need rules nobody has written for a role
12
+ * entry beside different assigneeRoles, two role entries, or a role beside people. So a role entry is
13
+ * refused by name - where an activity is saved, so whoever sets it reads why, and where a case is
14
+ * issued, for a list handed in or saved before this.
15
+ *
16
+ * The approval line is not this: there a role seat is "anyone of that role may judge" and is resolved
17
+ * as such (create-approval.ts).
18
+ */
19
+ type Entry = {
20
+ type?: string | null;
21
+ } | null | undefined;
22
+ export declare const ROLE_IS_NOT_AN_ASSIGNEE = "role-is-not-an-assignee";
23
+ /** null when the list may stand; ROLE_IS_NOT_AN_ASSIGNEE when an entry names a role. */
24
+ export declare function roleAssigneeRefusal(assignees: readonly Entry[] | null | undefined): typeof ROLE_IS_NOT_AN_ASSIGNEE | null;
25
+ export {};
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /**
3
+ * A role among the assignees of an activity or a case (ruling 2026-10-20).
4
+ *
5
+ * An assignee entry names one person. A role is many, and the entry made it one: issue() turned each
6
+ * entry into a person through getSystemUserFromOrgMemberItem, whose role branch answered the role's
7
+ * first user - an unordered relation, so whoever the database listed first got the work and the rest
8
+ * of the role never saw it.
9
+ *
10
+ * "Anyone of this role" already has its one place: `assigneeRoles`, on the activity and on the case.
11
+ * That is what the to-do lists and the pickable list read (my-open-threads.ts). Reading a role entry as
12
+ * that pool would make the pool live in two places, and would need rules nobody has written for a role
13
+ * entry beside different assigneeRoles, two role entries, or a role beside people. So a role entry is
14
+ * refused by name - where an activity is saved, so whoever sets it reads why, and where a case is
15
+ * issued, for a list handed in or saved before this.
16
+ *
17
+ * The approval line is not this: there a role seat is "anyone of that role may judge" and is resolved
18
+ * as such (create-approval.ts).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.ROLE_IS_NOT_AN_ASSIGNEE = void 0;
22
+ exports.roleAssigneeRefusal = roleAssigneeRefusal;
23
+ exports.ROLE_IS_NOT_AN_ASSIGNEE = 'role-is-not-an-assignee';
24
+ /** null when the list may stand; ROLE_IS_NOT_AN_ASSIGNEE when an entry names a role. */
25
+ function roleAssigneeRefusal(assignees) {
26
+ return (assignees || []).some(entry => entry?.type === 'Role') ? exports.ROLE_IS_NOT_AN_ASSIGNEE : null;
27
+ }
28
+ //# sourceMappingURL=assignee-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignee-input.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/assignee-input.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAOH,kDAEC;AALY,QAAA,uBAAuB,GAAG,yBAAyB,CAAA;AAEhE,wFAAwF;AACxF,SAAgB,mBAAmB,CAAC,SAA8C;IAChF,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,+BAAuB,CAAC,CAAC,CAAC,IAAI,CAAA;AACjG,CAAC","sourcesContent":["/**\n * A role among the assignees of an activity or a case (ruling 2026-10-20).\n *\n * An assignee entry names one person. A role is many, and the entry made it one: issue() turned each\n * entry into a person through getSystemUserFromOrgMemberItem, whose role branch answered the role's\n * first user - an unordered relation, so whoever the database listed first got the work and the rest\n * of the role never saw it.\n *\n * \"Anyone of this role\" already has its one place: `assigneeRoles`, on the activity and on the case.\n * That is what the to-do lists and the pickable list read (my-open-threads.ts). Reading a role entry as\n * that pool would make the pool live in two places, and would need rules nobody has written for a role\n * entry beside different assigneeRoles, two role entries, or a role beside people. So a role entry is\n * refused by name - where an activity is saved, so whoever sets it reads why, and where a case is\n * issued, for a list handed in or saved before this.\n *\n * The approval line is not this: there a role seat is \"anyone of that role may judge\" and is resolved\n * as such (create-approval.ts).\n */\n\ntype Entry = { type?: string | null } | null | undefined\n\nexport const ROLE_IS_NOT_AN_ASSIGNEE = 'role-is-not-an-assignee'\n\n/** null when the list may stand; ROLE_IS_NOT_AN_ASSIGNEE when an entry names a role. */\nexport function roleAssigneeRefusal(assignees: readonly Entry[] | null | undefined): typeof ROLE_IS_NOT_AN_ASSIGNEE | null {\n return (assignees || []).some(entry => entry?.type === 'Role') ? ROLE_IS_NOT_AN_ASSIGNEE : null\n}\n"]}
@@ -6,6 +6,7 @@ const activity_js_1 = require("../../service/activity/activity.js");
6
6
  const common_js_1 = require("../common.js");
7
7
  const entitlement_js_1 = require("./entitlement.js");
8
8
  const claim_draft_js_1 = require("./claim-draft.js");
9
+ const assignee_pool_js_1 = require("../../service/activity-instance/assignee-pool.js");
9
10
  const approval_line_input_js_1 = require("./approval-line-input.js");
10
11
  async function draft(activityInstance, context) {
11
12
  const { domain, user, tx } = context.state;
@@ -41,7 +42,7 @@ async function draft(activityInstance, context) {
41
42
  if (activityId) {
42
43
  activity = await repository.findOne({
43
44
  where: { domain: { id: domain.id }, id: activityId },
44
- relations: ['assigneeRole', 'supervisoryRole']
45
+ relations: ['assigneeRoles', 'supervisoryRole']
45
46
  });
46
47
  if (!activity) {
47
48
  throw new Error(context.t('error.activity not found', {
@@ -59,6 +60,10 @@ async function draft(activityInstance, context) {
59
60
  if (!activityInstance.assignees) {
60
61
  activityInstance.assignees = activity.assignees;
61
62
  }
63
+ /* A pool handed in is read in this domain, each role once - or refused by name (assignee-pool.ts). */
64
+ if (activityInstance.assigneeRoles) {
65
+ activityInstance.assigneeRoles = await (0, assignee_pool_js_1.resolvePoolRoles)(activityInstance.assigneeRoles, context);
66
+ }
62
67
  if (!activityInstance.approvalLine) {
63
68
  activityInstance.approvalLine = activity.approvalLine;
64
69
  }
@@ -73,7 +78,7 @@ async function draft(activityInstance, context) {
73
78
  uiSource: activity.uiSource,
74
79
  viewType: activity.viewType,
75
80
  viewSource: activity.viewSource,
76
- assigneeRole: activity.assigneeRole,
81
+ assigneeRoles: activity.assigneeRoles,
77
82
  supervisoryRole: activity.supervisoryRole,
78
83
  ...activityInstance,
79
84
  transaction: 'draft',
@@ -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'\nimport { assertMayWorkOnDraft } from './entitlement.js'\nimport { claimDraft } from './claim-draft.js'\nimport { LINE_IS_SET, approvalLineInputRefusal } from './approval-line-input.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 /* Held until this transaction ends, so a discard cannot remove it between the read and the save. */\n await claimDraft(id, context)\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 /*\n * The activity's approval line is set on its detail screen and nowhere else (ruling 2026-09-24).\n * A line handed in must be absent or the same one - § approval-line-input.ts.\n */\n if (approvalLineInputRefusal(activity.approvalLine, activityInstance.approvalLine)) {\n throw Object.assign(new Error(context.t('error.activity-instance approval line is set')), { code: LINE_IS_SET })\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'\nimport { claimDraft } from './claim-draft.js'\nimport { resolvePoolRoles } from '../../service/activity-instance/assignee-pool.js'\nimport { LINE_IS_SET, approvalLineInputRefusal } from './approval-line-input.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 /* Held until this transaction ends, so a discard cannot remove it between the read and the save. */\n await claimDraft(id, context)\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: ['assigneeRoles', '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 /*\n * The activity's approval line is set on its detail screen and nowhere else (ruling 2026-09-24).\n * A line handed in must be absent or the same one - § approval-line-input.ts.\n */\n if (approvalLineInputRefusal(activity.approvalLine, activityInstance.approvalLine)) {\n throw Object.assign(new Error(context.t('error.activity-instance approval line is set')), { code: LINE_IS_SET })\n }\n\n if (!activityInstance.assignees) {\n activityInstance.assignees = activity.assignees\n }\n /* A pool handed in is read in this domain, each role once - or refused by name (assignee-pool.ts). */\n if (activityInstance.assigneeRoles) {\n activityInstance.assigneeRoles = await resolvePoolRoles(activityInstance.assigneeRoles, context)\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 assigneeRoles: activity.assigneeRoles,\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"]}
@@ -13,7 +13,7 @@ async function end({ id, output, reason }, context) {
13
13
  'domain',
14
14
  'activity',
15
15
  'activityThreads',
16
- 'assigneeRole',
16
+ 'assigneeRoles',
17
17
  'supervisoryRole',
18
18
  'updater',
19
19
  'creator',
@@ -1 +1 @@
1
- {"version":3,"file":"end.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/end.ts"],"names":[],"mappings":";;AAIA,kBA4EC;AAhFD,+FAA+G;AAC/G,yFAAuF;AACvF,4CAAwD;AAEjD,KAAK,UAAU,GAAG,CACvB,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAoD,EACxE,OAAwB;IAExB,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;YACT,QAAQ;YACR,UAAU;YACV,iBAAiB;YACjB,cAAc;YACd,iBAAiB;YACjB,SAAS;YACT,SAAS;YACT,SAAS;YACT,YAAY;SACb;KACF,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;;;;MAIE;IACF,IACE,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,OAAO;QACxD,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,KAAK,EACtD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,+CAA+C,EAAE;YACzD,EAAE;YACF,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,yCAAoB,CAAC,OAAO,EAAE,yCAAoB,CAAC,KAAK,CAAC,CAAA;IAE7E,KAAK,IAAI,cAAc,IAAI,gBAAgB,CAAC,eAAe,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,SAAQ;QACV,CAAC;QAED,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,iDAAiD,EAAE;YAC3D,cAAc,EAAE,cAAc,CAAC,EAAE;YACjC,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC;QAChC,gBAAgB,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;IACzC,CAAC;IAED,OAAO,MAAM,UAAU,CAAC,IAAI,CAAC;QAC3B,GAAG,gBAAgB;QACnB,MAAM;QACN,MAAM;QACN,WAAW,EAAE,KAAK;QAClB,KAAK,EAAE,MAAM,IAAA,qCAAyB,EAAC,EAAE,EAAE,OAAO,CAAC;QACnD,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACV,CAAC,CAAA;AACX,CAAC","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { evalActivityInstanceState } from '../common.js'\n\nexport async function end(\n { id, output, reason }: { id: string; output?: object; reason?: string },\n context: ResolverContext\n): Promise<ActivityInstance> {\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: [\n 'domain',\n 'activity',\n 'activityThreads',\n 'assigneeRole',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\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 End.\n - The previous state of the task must not be End or Aborted.\n - There should be no unfinished activity threads.\n */\n if (\n activityInstance.state == ActivityInstanceStatus.Aborted ||\n activityInstance.state == ActivityInstanceStatus.Ended\n ) {\n throw new Error(\n context.t(`error.activity-instance is already terminated`, {\n id,\n state: activityInstance.state\n })\n )\n }\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n\n for (let activityThread of activityInstance.activityThreads) {\n if (TERMINATED.includes(activityThread.state)) {\n continue\n }\n\n throw new Error(\n context.t(`error.all activity threads should be terminated`, {\n activityThread: activityThread.id,\n state: activityInstance.state\n })\n )\n }\n\n if (!activityInstance.startedAt) {\n activityInstance.startedAt = new Date()\n }\n\n return await repository.save({\n ...activityInstance,\n reason,\n output,\n transaction: 'end',\n state: await evalActivityInstanceState(id, context),\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n } as any)\n}\n"]}
1
+ {"version":3,"file":"end.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/end.ts"],"names":[],"mappings":";;AAIA,kBA4EC;AAhFD,+FAA+G;AAC/G,yFAAuF;AACvF,4CAAwD;AAEjD,KAAK,UAAU,GAAG,CACvB,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAoD,EACxE,OAAwB;IAExB,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;YACT,QAAQ;YACR,UAAU;YACV,iBAAiB;YACjB,eAAe;YACf,iBAAiB;YACjB,SAAS;YACT,SAAS;YACT,SAAS;YACT,YAAY;SACb;KACF,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;;;;MAIE;IACF,IACE,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,OAAO;QACxD,gBAAgB,CAAC,KAAK,IAAI,6CAAsB,CAAC,KAAK,EACtD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,+CAA+C,EAAE;YACzD,EAAE;YACF,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,yCAAoB,CAAC,OAAO,EAAE,yCAAoB,CAAC,KAAK,CAAC,CAAA;IAE7E,KAAK,IAAI,cAAc,IAAI,gBAAgB,CAAC,eAAe,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,SAAQ;QACV,CAAC;QAED,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,iDAAiD,EAAE;YAC3D,cAAc,EAAE,cAAc,CAAC,EAAE;YACjC,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B,CAAC,CACH,CAAA;IACH,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC;QAChC,gBAAgB,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;IACzC,CAAC;IAED,OAAO,MAAM,UAAU,CAAC,IAAI,CAAC;QAC3B,GAAG,gBAAgB;QACnB,MAAM;QACN,MAAM;QACN,WAAW,EAAE,KAAK;QAClB,KAAK,EAAE,MAAM,IAAA,qCAAyB,EAAC,EAAE,EAAE,OAAO,CAAC;QACnD,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACV,CAAC,CAAA;AACX,CAAC","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { evalActivityInstanceState } from '../common.js'\n\nexport async function end(\n { id, output, reason }: { id: string; output?: object; reason?: string },\n context: ResolverContext\n): Promise<ActivityInstance> {\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: [\n 'domain',\n 'activity',\n 'activityThreads',\n 'assigneeRoles',\n 'supervisoryRole',\n 'updater',\n 'creator',\n 'starter',\n 'terminator'\n ]\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 End.\n - The previous state of the task must not be End or Aborted.\n - There should be no unfinished activity threads.\n */\n if (\n activityInstance.state == ActivityInstanceStatus.Aborted ||\n activityInstance.state == ActivityInstanceStatus.Ended\n ) {\n throw new Error(\n context.t(`error.activity-instance is already terminated`, {\n id,\n state: activityInstance.state\n })\n )\n }\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n\n for (let activityThread of activityInstance.activityThreads) {\n if (TERMINATED.includes(activityThread.state)) {\n continue\n }\n\n throw new Error(\n context.t(`error.all activity threads should be terminated`, {\n activityThread: activityThread.id,\n state: activityInstance.state\n })\n )\n }\n\n if (!activityInstance.startedAt) {\n activityInstance.startedAt = new Date()\n }\n\n return await repository.save({\n ...activityInstance,\n reason,\n output,\n transaction: 'end',\n state: await evalActivityInstanceState(id, context),\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n } as any)\n}\n"]}
@@ -11,7 +11,7 @@ export declare function assertMayWorkOnDraft(activityInstance: ActivityInstance,
11
11
  * May this person pick up this task?
12
12
  *
13
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
14
+ * `assigneeRoles` is how an issuer says "this is for the quality inspectors". Reading it is
15
15
  * the whole point of writing it, and until now it was loaded and ignored.
16
16
  *
17
17
  * ── When no role is named, anyone in the domain may take it ──────────────────
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertMayWorkOnDraft = assertMayWorkOnDraft;
4
4
  exports.assertMayPick = assertMayPick;
5
5
  const auth_base_1 = require("@things-factory/auth-base");
6
+ const assignee_pool_js_1 = require("../../service/activity-instance/assignee-pool.js");
6
7
  /**
7
8
  * Who may act on **this one** activity instance.
8
9
  *
@@ -31,7 +32,7 @@ const auth_base_1 = require("@things-factory/auth-base");
31
32
  *
32
33
  * draft(id) edits a draft — never checked who wrote it
33
34
  * 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
+ * pick(id) takes an unassigned task — loaded the assignee role and never read it
35
36
  *
36
37
  * The category gate cannot fix that. Turning it on locks out the operator whose task it is;
37
38
  * leaving it off lets anyone in the domain take, edit or issue anyone else's. Those are two
@@ -74,7 +75,7 @@ async function assertMayWorkOnDraft(activityInstance, context) {
74
75
  * May this person pick up this task?
75
76
  *
76
77
  * ── 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
+ * `assigneeRoles` is how an issuer says "this is for the quality inspectors". Reading it is
78
79
  * the whole point of writing it, and until now it was loaded and ignored.
79
80
  *
80
81
  * ── When no role is named, anyone in the domain may take it ──────────────────
@@ -87,15 +88,16 @@ async function assertMayWorkOnDraft(activityInstance, context) {
87
88
  */
88
89
  async function assertMayPick(activityInstance, context) {
89
90
  const { user, tx } = context.state;
90
- const roleId = activityInstance.assigneeRoleId;
91
- if (!roleId) {
91
+ const poolIds = (0, assignee_pool_js_1.poolIdsOf)(activityInstance);
92
+ if (!poolIds.length) {
92
93
  return;
93
94
  }
94
95
  const holder = await tx.getRepository(auth_base_1.User).findOne({
95
96
  where: { id: user.id },
96
97
  relations: ['roles']
97
98
  });
98
- if ((holder?.roles || []).some((role) => role.id === roleId)) {
99
+ /* Any one of the pool's roles is enough (ADR-0091 decision 9). */
100
+ if ((0, assignee_pool_js_1.holdsAnyOf)((holder?.roles || []).map((role) => role.id), poolIds)) {
99
101
  return;
100
102
  }
101
103
  if (await manages(context)) {