@things-factory/worklist 10.1.270 → 10.1.272

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 (65) hide show
  1. package/dist-client/bootstrap.d.ts +1 -0
  2. package/dist-client/bootstrap.js +1 -0
  3. package/dist-client/bootstrap.js.map +1 -1
  4. package/dist-client/components/activity-thread-timeline.d.ts +2 -2
  5. package/dist-client/components/person-question-todo.d.ts +16 -0
  6. package/dist-client/components/person-question-todo.js +75 -0
  7. package/dist-client/components/person-question-todo.js.map +1 -0
  8. package/dist-client/components/source-link.d.ts +1 -1
  9. package/dist-client/pages/activity-instance/activity-instance-view.d.ts +1 -1
  10. package/dist-client/pages/activity-thread/activity-thread-view.d.ts +1 -1
  11. package/dist-client/route.d.ts +1 -1
  12. package/dist-client/tsconfig.tsbuildinfo +1 -1
  13. package/dist-server/controllers/activity-approval/_abort.js +2 -0
  14. package/dist-server/controllers/activity-approval/_abort.js.map +1 -1
  15. package/dist-server/controllers/activity-approval/claim.js +6 -1
  16. package/dist-server/controllers/activity-approval/claim.js.map +1 -1
  17. package/dist-server/controllers/activity-instance/complete.js +4 -0
  18. package/dist-server/controllers/activity-instance/complete.js.map +1 -1
  19. package/dist-server/controllers/activity-instance/hand-out.d.ts +2 -0
  20. package/dist-server/controllers/activity-instance/hand-out.js +7 -0
  21. package/dist-server/controllers/activity-instance/hand-out.js.map +1 -1
  22. package/dist-server/controllers/common.js +2 -0
  23. package/dist-server/controllers/common.js.map +1 -1
  24. package/dist-server/service/activity/index.d.ts +1 -1
  25. package/dist-server/service/activity-approval/index.d.ts +1 -1
  26. package/dist-server/service/activity-instance/index.d.ts +1 -1
  27. package/dist-server/service/activity-template/index.d.ts +1 -1
  28. package/dist-server/service/activity-thread/index.d.ts +1 -1
  29. package/dist-server/service/approval-notice/approval-notice-rules.d.ts +9 -0
  30. package/dist-server/service/approval-notice/approval-notice-rules.js +25 -1
  31. package/dist-server/service/approval-notice/approval-notice-rules.js.map +1 -1
  32. package/dist-server/service/approval-notice/approval-notice.d.ts +22 -0
  33. package/dist-server/service/approval-notice/approval-notice.js +46 -0
  34. package/dist-server/service/approval-notice/approval-notice.js.map +1 -1
  35. package/dist-server/service/index.d.ts +5 -3
  36. package/dist-server/service/index.js +12 -2
  37. package/dist-server/service/index.js.map +1 -1
  38. package/dist-server/service/person-question/index.d.ts +4 -0
  39. package/dist-server/service/person-question/index.js +19 -0
  40. package/dist-server/service/person-question/index.js.map +1 -0
  41. package/dist-server/service/person-question/person-question-activity.d.ts +67 -0
  42. package/dist-server/service/person-question/person-question-activity.js +46 -0
  43. package/dist-server/service/person-question/person-question-activity.js.map +1 -0
  44. package/dist-server/service/person-question/person-question-card.d.ts +1 -0
  45. package/dist-server/service/person-question/person-question-card.js +36 -0
  46. package/dist-server/service/person-question/person-question-card.js.map +1 -0
  47. package/dist-server/service/person-question/person-question-resolver.d.ts +10 -0
  48. package/dist-server/service/person-question/person-question-resolver.js +54 -0
  49. package/dist-server/service/person-question/person-question-resolver.js.map +1 -0
  50. package/dist-server/service/person-question/person-question-service.d.ts +17 -0
  51. package/dist-server/service/person-question/person-question-service.js +86 -0
  52. package/dist-server/service/person-question/person-question-service.js.map +1 -0
  53. package/dist-server/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +11 -10
  55. package/tests/approval-notice-db.test.ts +3 -1
  56. package/tests/complete-db.test.ts +2 -1
  57. package/tests/delegate-approval.test.ts +3 -1
  58. package/tests/hand-out-notice-db.test.ts +99 -0
  59. package/tests/person-question-db.test.ts +158 -0
  60. package/translations/en.json +21 -1
  61. package/translations/es.json +21 -1
  62. package/translations/ja.json +21 -1
  63. package/translations/ko.json +21 -1
  64. package/translations/ms.json +21 -1
  65. package/translations/zh.json +21 -1
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._abort = _abort;
4
4
  const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
5
5
  const opinion_js_1 = require("./opinion.js");
6
+ const approval_notice_js_1 = require("../../service/approval-notice/approval-notice.js");
6
7
  const activity_approval_js_1 = require("../../service/activity-approval/activity-approval.js");
7
8
  async function _abort({ activityApproval, comment }, context) {
8
9
  const { user, tx } = context.state;
@@ -24,6 +25,7 @@ async function _abort({ activityApproval, comment }, context) {
24
25
  terminatedAt: new Date(),
25
26
  terminator: user
26
27
  });
28
+ await (0, approval_notice_js_1.closeApprovalNotices)(tx, [activityApproval]);
27
29
  return result;
28
30
  }
29
31
  //# sourceMappingURL=_abort.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_abort.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/_abort.ts"],"names":[],"mappings":";;AAIA,wBA2BC;AA/BD,yFAAuG;AACvG,6CAA6C;AAC7C,+FAAiH;AAE1G,KAAK,UAAU,MAAM,CAC1B,EAAE,gBAAgB,EAAE,OAAO,EAA2D,EACtF,OAAwB;IAExB,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAElC,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAC,IAAI,CAAC;QACzD,GAAG,gBAAgB;QACnB,QAAQ,EAAE,+CAAwB,CAAC,OAAO;QAC1C,GAAG,IAAA,2BAAc,EAAC,OAAO,CAAC;QAC1B,WAAW,EAAE,OAAO;QACpB,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;IAEF,MAAM,EAAE,CAAC,aAAa,CAAC,mCAAc,CAAC,CAAC,IAAI,CAAC;QAC1C,GAAG,gBAAgB,CAAC,cAAc;QAClC,WAAW,EAAE,OAAO;QACpB,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,yCAAoB,CAAC,OAAO;QACnC,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { ActivityThread, ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { stampedComment } from './opinion.js'\nimport { ActivityApproval, ActivityApprovalJudgment } from '../../service/activity-approval/activity-approval.js'\n\nexport async function _abort(\n { activityApproval, comment }: { activityApproval: ActivityApproval; comment: string },\n context: ResolverContext\n): Promise<ActivityApproval> {\n const { user, tx } = context.state\n\n var result = await tx.getRepository(ActivityApproval).save({\n ...activityApproval,\n judgment: ActivityApprovalJudgment.Aborted,\n ...stampedComment(comment),\n transaction: 'abort',\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n\n await tx.getRepository(ActivityThread).save({\n ...activityApproval.activityThread,\n transaction: 'abort',\n reason: comment,\n state: ActivityThreadStatus.Aborted,\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n\n return result\n}\n"]}
1
+ {"version":3,"file":"_abort.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/_abort.ts"],"names":[],"mappings":";;AAKA,wBA4BC;AAjCD,yFAAuG;AACvG,6CAA6C;AAC7C,yFAAuF;AACvF,+FAAiH;AAE1G,KAAK,UAAU,MAAM,CAC1B,EAAE,gBAAgB,EAAE,OAAO,EAA2D,EACtF,OAAwB;IAExB,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAElC,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,uCAAgB,CAAC,CAAC,IAAI,CAAC;QACzD,GAAG,gBAAgB;QACnB,QAAQ,EAAE,+CAAwB,CAAC,OAAO;QAC1C,GAAG,IAAA,2BAAc,EAAC,OAAO,CAAC;QAC1B,WAAW,EAAE,OAAO;QACpB,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;IAEF,MAAM,EAAE,CAAC,aAAa,CAAC,mCAAc,CAAC,CAAC,IAAI,CAAC;QAC1C,GAAG,gBAAgB,CAAC,cAAc;QAClC,WAAW,EAAE,OAAO;QACpB,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,yCAAoB,CAAC,OAAO;QACnC,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI,IAAI,EAAE;QACxB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;IAEF,MAAM,IAAA,yCAAoB,EAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAA;IAClD,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { ActivityThread, ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { stampedComment } from './opinion.js'\nimport { closeApprovalNotices } from '../../service/approval-notice/approval-notice.js'\nimport { ActivityApproval, ActivityApprovalJudgment } from '../../service/activity-approval/activity-approval.js'\n\nexport async function _abort(\n { activityApproval, comment }: { activityApproval: ActivityApproval; comment: string },\n context: ResolverContext\n): Promise<ActivityApproval> {\n const { user, tx } = context.state\n\n var result = await tx.getRepository(ActivityApproval).save({\n ...activityApproval,\n judgment: ActivityApprovalJudgment.Aborted,\n ...stampedComment(comment),\n transaction: 'abort',\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n\n await tx.getRepository(ActivityThread).save({\n ...activityApproval.activityThread,\n transaction: 'abort',\n reason: comment,\n state: ActivityThreadStatus.Aborted,\n updater: user,\n terminatedAt: new Date(),\n terminator: user\n })\n\n await closeApprovalNotices(tx, [activityApproval])\n return result\n}\n"]}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.claimPendingApproval = claimPendingApproval;
4
4
  const activity_approval_js_1 = require("../../service/activity-approval/activity-approval.js");
5
+ const approval_notice_js_1 = require("../../service/approval-notice/approval-notice.js");
5
6
  /**
6
7
  * 이 결재를 내가 잡는다 — **대기 중인 것만, 한 번만.**
7
8
  *
@@ -72,6 +73,10 @@ async function claimPendingApproval(tx, approval, judgment) {
72
73
  * Elsewhere the same `?? 0` was a defect — a missing count drawn as 「0 waiting」 — so the two
73
74
  * are worth telling apart rather than tidying into one habit.
74
75
  */
75
- return (result.affected ?? 0) > 0;
76
+ const claimed = (result.affected ?? 0) > 0;
77
+ /* the turn is no one's any more - its notices leave the screens (ADR-0164 amendment 1) */
78
+ if (claimed)
79
+ await (0, approval_notice_js_1.closeApprovalNotices)(tx, [approval]);
80
+ return claimed;
76
81
  }
77
82
  //# sourceMappingURL=claim.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claim.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/claim.ts"],"names":[],"mappings":";;AA2CA,oDAsCC;AA/ED,+FAAiH;AAEjH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACI,KAAK,UAAU,oBAAoB,CACxC,EAAiB,EACjB,QAA0B,EAC1B,QAAkC;IAElC;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAA;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,kBAAkB,EAAE;SACpB,MAAM,CAAC,uCAAgB,CAAC;SACxB,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;SACjB,KAAK,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;QACvC;;;WAGG;SACF,QAAQ,CAAC,2CAA2C,EAAE,EAAE,OAAO,EAAE,+CAAwB,CAAC,OAAO,EAAE,CAAC;SACpG,OAAO,EAAE,CAAA;IAEZ;;;;;;;;;;OAUG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;AACnC,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { ActivityApproval, ActivityApprovalJudgment } from '../../service/activity-approval/activity-approval.js'\n\n/**\n * 이 결재를 내가 잡는다 — **대기 중인 것만, 한 번만.**\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 승인·반려·위임이 다 같은 모양으로 시작한다. 읽고 → 「대기 중인가」 보고 → 저장한다. 그 사이에 아무\n * 잠금이 없다.\n *\n * 역할로 걸린 결재는 **여러 명이 같은 건을 본다.** 대기 목록에 뜬 것을 두 사람이 누르는 것은 공유\n * 대기열의 평범한 실패 방식이다. 지금은 둘 다 검사를 통과하고 **둘 다 다음 결재자를 만든다** — 같은\n * 차례의 결재가 두 벌 서서 결재선이 갈라진다. 오류는 안 난다.\n *\n * ── 검사와 쓰기를 한 문장으로 ──────────────────────────────────────────────\n * `WHERE judgment = ''` 를 갱신에 붙인다. 늦게 온 쪽은 0줄을 고치고, 그것으로 「남이 먼저 처리했다」를\n * 안다.\n *\n * 두 트랜잭션이 겹치면 뒤엣것이 행 잠금 앞에서 멈춰 섰다가, 앞엣것이 커밋한 뒤 조건을 **다시 보고**\n * 0줄을 얻는다. 세 드라이버 다 그렇게 동작한다 — postgres 는 잠금이 풀린 뒤 조건을 다시 보고, mysql\n * InnoDB 는 갱신 시 최신 커밋본을 읽고, sqlite 는 쓰기를 아예 직렬화한다.\n *\n * ── 잡고 나면 그 행은 이 트랜잭션 것이다 ───────────────────────────────────\n * 잡는 순간 행 잠금을 쥐므로, 나머지는 평범하게 저장해도 된다. 그래서 여기서는 **판정 하나만** 원자적으로\n * 바꾼다 — 지키려는 규칙이 「대기 중인 것만 처리할 수 있다」 하나이기 때문이다.\n *\n * ── 왜 `@VersionColumn` 이 아닌가 ──────────────────────────────────────────\n * 버전 칸은 컬럼을 늘려 마이그레이션이 필요하고, 이 규칙과 무관한 저장(의견 수정)까지 충돌시킨다.\n * 이미 코드에 있는 검사를 그대로 갱신 문장으로 옮기는 편이 정확하고, 스키마를 안 건드려 배포해도\n * 기존 데이터에 영향이 없다. (2026-08-31, 사용자 결정)\n *\n * ── 잡은 뒤의 저장은 안전하다 ─────────────────────────────────────────────\n * 부르는 쪽은 잡기 뒤에 `save({ ...approval, judgment, comment, … })` 로 나머지를 쓴다. 손에 든\n * `approval` 은 잡기 **전에** 읽은 것이라 판정이 낡았는데, 그 저장이 판정을 다시 명시하므로 되돌리지\n * 않는다. 그리고 잡는 순간 행 잠금을 쥐었으므로 그 사이에 남이 끼어들 수 없다.\n *\n * 한 문장으로 합치지 않은 이유는 관계 칸 때문이다 — `updater`·`terminator`·`originalApproval` 이\n * 엔티티 객체이고, `save()` 는 이것을 FK 로 풀어 주지만 `UpdateQueryBuilder.set()` 은 버전에 따라\n * 다르다. 풀리지 않으면 「누가 처리했나」가 비고 **오류는 안 난다.**\n *\n * @returns 잡았으면 참. 거짓이면 **남이 먼저 처리했다** — 부르는 쪽이 던져서 트랜잭션을 물려야 한다.\n */\nexport async function claimPendingApproval(\n tx: EntityManager,\n approval: ActivityApproval,\n judgment: ActivityApprovalJudgment\n): Promise<boolean> {\n /*\n * 대기로 잡으면 조건이 통과하는데 판정이 그대로라 **두 번째 호출도 통과한다.** 잡는 뜻이 없어진다.\n *\n * `Pending` 은 빈 문자열이라 이 한 줄에 함께 걸린다 — 타입 검사도 그렇게 좁혀 준다.\n */\n if (!judgment) {\n throw new Error('claimPendingApproval: a claim must move the judgment out of pending')\n }\n\n const result = await tx\n .createQueryBuilder()\n .update(ActivityApproval)\n .set({ judgment })\n .where('id = :id', { id: approval.id })\n /*\n * 대기 중은 빈 문자열이다(`ActivityApprovalJudgment.Pending`). `IS NULL` 도 함께 보는 이유는 기본값이\n * 안 들어간 옛 행이 있을 수 있어서다 — 그런 행을 못 잡으면 그 결재는 영영 처리되지 않는다.\n */\n .andWhere('(judgment = :pending OR judgment IS NULL)', { pending: ActivityApprovalJudgment.Pending })\n .execute()\n\n /*\n * `?? 0` here is deliberate, and it is the opposite of the same expression elsewhere.\n *\n * A missing row count means we do not know whether this claim took. For a conditional update\n * that hands one person an approval, not knowing has to read as \"did not get it\": refusing a\n * claim costs a retry, while granting one on an unknown result lets two people judge the same\n * request. The unknown value maps to the conservative answer on purpose.\n *\n * Elsewhere the same `?? 0` was a defect — a missing count drawn as 「0 waiting」 — so the two\n * are worth telling apart rather than tidying into one habit.\n */\n return (result.affected ?? 0) > 0\n}\n"]}
1
+ {"version":3,"file":"claim.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/claim.ts"],"names":[],"mappings":";;AA4CA,oDAyCC;AAnFD,+FAAiH;AACjH,yFAAuF;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACI,KAAK,UAAU,oBAAoB,CACxC,EAAiB,EACjB,QAA0B,EAC1B,QAAkC;IAElC;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAA;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,kBAAkB,EAAE;SACpB,MAAM,CAAC,uCAAgB,CAAC;SACxB,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;SACjB,KAAK,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;QACvC;;;WAGG;SACF,QAAQ,CAAC,2CAA2C,EAAE,EAAE,OAAO,EAAE,+CAAwB,CAAC,OAAO,EAAE,CAAC;SACpG,OAAO,EAAE,CAAA;IAEZ;;;;;;;;;;OAUG;IACH,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IAC1C,0FAA0F;IAC1F,IAAI,OAAO;QAAE,MAAM,IAAA,yCAAoB,EAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IACvD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { ActivityApproval, ActivityApprovalJudgment } from '../../service/activity-approval/activity-approval.js'\nimport { closeApprovalNotices } from '../../service/approval-notice/approval-notice.js'\n\n/**\n * 이 결재를 내가 잡는다 — **대기 중인 것만, 한 번만.**\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 승인·반려·위임이 다 같은 모양으로 시작한다. 읽고 → 「대기 중인가」 보고 → 저장한다. 그 사이에 아무\n * 잠금이 없다.\n *\n * 역할로 걸린 결재는 **여러 명이 같은 건을 본다.** 대기 목록에 뜬 것을 두 사람이 누르는 것은 공유\n * 대기열의 평범한 실패 방식이다. 지금은 둘 다 검사를 통과하고 **둘 다 다음 결재자를 만든다** — 같은\n * 차례의 결재가 두 벌 서서 결재선이 갈라진다. 오류는 안 난다.\n *\n * ── 검사와 쓰기를 한 문장으로 ──────────────────────────────────────────────\n * `WHERE judgment = ''` 를 갱신에 붙인다. 늦게 온 쪽은 0줄을 고치고, 그것으로 「남이 먼저 처리했다」를\n * 안다.\n *\n * 두 트랜잭션이 겹치면 뒤엣것이 행 잠금 앞에서 멈춰 섰다가, 앞엣것이 커밋한 뒤 조건을 **다시 보고**\n * 0줄을 얻는다. 세 드라이버 다 그렇게 동작한다 — postgres 는 잠금이 풀린 뒤 조건을 다시 보고, mysql\n * InnoDB 는 갱신 시 최신 커밋본을 읽고, sqlite 는 쓰기를 아예 직렬화한다.\n *\n * ── 잡고 나면 그 행은 이 트랜잭션 것이다 ───────────────────────────────────\n * 잡는 순간 행 잠금을 쥐므로, 나머지는 평범하게 저장해도 된다. 그래서 여기서는 **판정 하나만** 원자적으로\n * 바꾼다 — 지키려는 규칙이 「대기 중인 것만 처리할 수 있다」 하나이기 때문이다.\n *\n * ── 왜 `@VersionColumn` 이 아닌가 ──────────────────────────────────────────\n * 버전 칸은 컬럼을 늘려 마이그레이션이 필요하고, 이 규칙과 무관한 저장(의견 수정)까지 충돌시킨다.\n * 이미 코드에 있는 검사를 그대로 갱신 문장으로 옮기는 편이 정확하고, 스키마를 안 건드려 배포해도\n * 기존 데이터에 영향이 없다. (2026-08-31, 사용자 결정)\n *\n * ── 잡은 뒤의 저장은 안전하다 ─────────────────────────────────────────────\n * 부르는 쪽은 잡기 뒤에 `save({ ...approval, judgment, comment, … })` 로 나머지를 쓴다. 손에 든\n * `approval` 은 잡기 **전에** 읽은 것이라 판정이 낡았는데, 그 저장이 판정을 다시 명시하므로 되돌리지\n * 않는다. 그리고 잡는 순간 행 잠금을 쥐었으므로 그 사이에 남이 끼어들 수 없다.\n *\n * 한 문장으로 합치지 않은 이유는 관계 칸 때문이다 — `updater`·`terminator`·`originalApproval` 이\n * 엔티티 객체이고, `save()` 는 이것을 FK 로 풀어 주지만 `UpdateQueryBuilder.set()` 은 버전에 따라\n * 다르다. 풀리지 않으면 「누가 처리했나」가 비고 **오류는 안 난다.**\n *\n * @returns 잡았으면 참. 거짓이면 **남이 먼저 처리했다** — 부르는 쪽이 던져서 트랜잭션을 물려야 한다.\n */\nexport async function claimPendingApproval(\n tx: EntityManager,\n approval: ActivityApproval,\n judgment: ActivityApprovalJudgment\n): Promise<boolean> {\n /*\n * 대기로 잡으면 조건이 통과하는데 판정이 그대로라 **두 번째 호출도 통과한다.** 잡는 뜻이 없어진다.\n *\n * `Pending` 은 빈 문자열이라 이 한 줄에 함께 걸린다 — 타입 검사도 그렇게 좁혀 준다.\n */\n if (!judgment) {\n throw new Error('claimPendingApproval: a claim must move the judgment out of pending')\n }\n\n const result = await tx\n .createQueryBuilder()\n .update(ActivityApproval)\n .set({ judgment })\n .where('id = :id', { id: approval.id })\n /*\n * 대기 중은 빈 문자열이다(`ActivityApprovalJudgment.Pending`). `IS NULL` 도 함께 보는 이유는 기본값이\n * 안 들어간 옛 행이 있을 수 있어서다 — 그런 행을 못 잡으면 그 결재는 영영 처리되지 않는다.\n */\n .andWhere('(judgment = :pending OR judgment IS NULL)', { pending: ActivityApprovalJudgment.Pending })\n .execute()\n\n /*\n * `?? 0` here is deliberate, and it is the opposite of the same expression elsewhere.\n *\n * A missing row count means we do not know whether this claim took. For a conditional update\n * that hands one person an approval, not knowing has to read as \"did not get it\": refusing a\n * claim costs a retry, while granting one on an unknown result lets two people judge the same\n * request. The unknown value maps to the conservative answer on purpose.\n *\n * Elsewhere the same `?? 0` was a defect — a missing count drawn as 「0 waiting」 — so the two\n * are worth telling apart rather than tidying into one habit.\n */\n const claimed = (result.affected ?? 0) > 0\n /* the turn is no one's any more - its notices leave the screens (ADR-0164 amendment 1) */\n if (claimed) await closeApprovalNotices(tx, [approval])\n return claimed\n}\n"]}
@@ -4,6 +4,8 @@ exports.complete = complete;
4
4
  const typeorm_1 = require("typeorm");
5
5
  const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
6
6
  const activity_instance_js_1 = require("../../service/activity-instance/activity-instance.js");
7
+ const notification_1 = require("@things-factory/notification");
8
+ const approval_notice_rules_js_1 = require("../../service/approval-notice/approval-notice-rules.js");
7
9
  const common_js_1 = require("../common.js");
8
10
  /**
9
11
  * Ends a case whose work is done - every thread still open ends as done, then the case ends. The counterpart of
@@ -46,6 +48,8 @@ async function complete({ id, reason, output }, context) {
46
48
  updater: user,
47
49
  terminatedAt: now
48
50
  });
51
+ /* Its work done, the notice that handed it out is closed (ADR-0200 decision 3). */
52
+ await (0, notification_1.closeNotices)(tx, domain, 'todo', (0, approval_notice_rules_js_1.todoNoticeKey)(thread.id));
49
53
  }
50
54
  }
51
55
  return await repository.save({
@@ -1 +1 @@
1
- {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/complete.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { ActivityThread, ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { evalActivityInstanceState } from '../common.js'\n\n/**\n * Ends a case whose work is done - every thread still open ends as done, then the case ends. The counterpart of\n * `abort` for a to-do the records show finished: a case handed to several people, or to whoever holds a role, is\n * done for all of them when its work is done, and its history says ended, not aborted.\n *\n * For the module that issued the case and judges its work (an application's own to-do); a person ends their own\n * thread through the thread's `end`.\n *\n * `output` is what was done, in the activity's declared output fields - the finished case shows it, as it shows what a\n * person wrote when they end their own thread. A module that closed its to-do with only a reason left the finished\n * screen with nothing to show of how it ended (2026-11-11, 문의 할 일).\n */\nexport async function complete(\n { id, reason, output }: { id: string; reason?: string; output?: Record<string, any> },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(ActivityInstance)\n\n const activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activity', 'activityThreads', 'assigneeRoles', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']\n })\n\n if (!activityInstance) {\n throw new Error(context.t('error.activity-instance not found', { activityInstance: id }))\n }\n\n if (activityInstance.state == ActivityInstanceStatus.Aborted || activityInstance.state == ActivityInstanceStatus.Ended) {\n throw new Error(context.t(`error.activity-instance is already terminated`, { id, state: activityInstance.state }))\n }\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n const open = (activityInstance.activityThreads ?? []).filter(thread => !TERMINATED.includes(thread.state))\n const now = new Date()\n if (open.length) {\n const threads = tx.getRepository(ActivityThread)\n for (const thread of await threads.find({ where: { id: In(open.map(one => one.id)) } })) {\n await threads.save({\n ...thread,\n reason,\n ...(output ? { output: { ...(thread.output ?? {}), ...output } } : {}),\n state: ActivityThreadStatus.Ended,\n transaction: 'end',\n startedAt: thread.startedAt ?? now,\n updater: user,\n terminatedAt: now\n })\n }\n }\n\n return await repository.save({\n ...activityInstance,\n activityThreads: undefined,\n reason,\n ...(await evalActivityInstanceState(id, context)),\n startedAt: activityInstance.startedAt ?? now,\n transaction: 'end',\n updater: user,\n terminatedAt: now,\n terminator: user\n } as any)\n}\n"]}
1
+ {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/complete.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { ActivityThread, ActivityThreadStatus } from '../../service/activity-thread/activity-thread.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../../service/activity-instance/activity-instance.js'\nimport { closeNotices } from '@things-factory/notification'\n\nimport { todoNoticeKey } from '../../service/approval-notice/approval-notice-rules.js'\nimport { evalActivityInstanceState } from '../common.js'\n\n/**\n * Ends a case whose work is done - every thread still open ends as done, then the case ends. The counterpart of\n * `abort` for a to-do the records show finished: a case handed to several people, or to whoever holds a role, is\n * done for all of them when its work is done, and its history says ended, not aborted.\n *\n * For the module that issued the case and judges its work (an application's own to-do); a person ends their own\n * thread through the thread's `end`.\n *\n * `output` is what was done, in the activity's declared output fields - the finished case shows it, as it shows what a\n * person wrote when they end their own thread. A module that closed its to-do with only a reason left the finished\n * screen with nothing to show of how it ended (2026-11-11, 문의 할 일).\n */\nexport async function complete(\n { id, reason, output }: { id: string; reason?: string; output?: Record<string, any> },\n context: ResolverContext\n): Promise<ActivityInstance> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(ActivityInstance)\n\n const activityInstance = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activity', 'activityThreads', 'assigneeRoles', 'supervisoryRole', 'updater', 'creator', 'starter', 'terminator']\n })\n\n if (!activityInstance) {\n throw new Error(context.t('error.activity-instance not found', { activityInstance: id }))\n }\n\n if (activityInstance.state == ActivityInstanceStatus.Aborted || activityInstance.state == ActivityInstanceStatus.Ended) {\n throw new Error(context.t(`error.activity-instance is already terminated`, { id, state: activityInstance.state }))\n }\n\n const TERMINATED = [ActivityThreadStatus.Aborted, ActivityThreadStatus.Ended]\n const open = (activityInstance.activityThreads ?? []).filter(thread => !TERMINATED.includes(thread.state))\n const now = new Date()\n if (open.length) {\n const threads = tx.getRepository(ActivityThread)\n for (const thread of await threads.find({ where: { id: In(open.map(one => one.id)) } })) {\n await threads.save({\n ...thread,\n reason,\n ...(output ? { output: { ...(thread.output ?? {}), ...output } } : {}),\n state: ActivityThreadStatus.Ended,\n transaction: 'end',\n startedAt: thread.startedAt ?? now,\n updater: user,\n terminatedAt: now\n })\n /* Its work done, the notice that handed it out is closed (ADR-0200 decision 3). */\n await closeNotices(tx, domain, 'todo', todoNoticeKey(thread.id))\n }\n }\n\n return await repository.save({\n ...activityInstance,\n activityThreads: undefined,\n reason,\n ...(await evalActivityInstanceState(id, context)),\n startedAt: activityInstance.startedAt ?? now,\n transaction: 'end',\n updater: user,\n terminatedAt: now,\n terminator: user\n } as any)\n}\n"]}
@@ -19,5 +19,7 @@ export interface HandOut {
19
19
  *
20
20
  * `context.state.user` is who set it off - the manager who assigned the course, or the person whose turn came - and
21
21
  * stands as the issuer.
22
+ *
23
+ * The person is told (ADR-0200 decision 3) - the to-do opens from the notice - unless they handed it to themselves.
22
24
  */
23
25
  export declare function handOut(todo: HandOut, context: ResolverContext): Promise<ActivityInstance>;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handOut = handOut;
4
+ const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
5
+ const approval_notice_js_1 = require("../../service/approval-notice/approval-notice.js");
4
6
  const assign_js_1 = require("./assign.js");
5
7
  const issue_js_1 = require("./issue.js");
6
8
  /**
@@ -11,11 +13,16 @@ const issue_js_1 = require("./issue.js");
11
13
  *
12
14
  * `context.state.user` is who set it off - the manager who assigned the course, or the person whose turn came - and
13
15
  * stands as the issuer.
16
+ *
17
+ * The person is told (ADR-0200 decision 3) - the to-do opens from the notice - unless they handed it to themselves.
14
18
  */
15
19
  async function handOut(todo, context) {
16
20
  const { activityId, name, description, input, assignee, dueAt } = todo;
17
21
  const instance = await (0, issue_js_1.issue)({ name, description: description ?? name, activityId, input, assignees: [], threadsMin: 0, dueAt, creator: null }, context);
18
22
  await (0, assign_js_1.assign)({ id: instance.id, assignees: [assignee.email], dueAt, reason: name }, context);
23
+ const thread = await context.state.tx.getRepository(activity_thread_js_1.ActivityThread).findOne({ where: { activityInstance: { id: instance.id }, assignee: { id: assignee.id } } });
24
+ if (thread)
25
+ (0, approval_notice_js_1.noticeHandOut)(context, { threadId: thread.id, name, assignee });
19
26
  return instance;
20
27
  }
21
28
  //# sourceMappingURL=hand-out.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hand-out.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/hand-out.ts"],"names":[],"mappings":";;AA2BA,0BAKC;AA7BD,2CAAoC;AACpC,yCAAkC;AAclC;;;;;;;;GAQG;AACI,KAAK,UAAU,OAAO,CAAC,IAAa,EAAE,OAAwB;IACnE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IACtE,MAAM,QAAQ,GAAG,MAAM,IAAA,gBAAK,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAS,EAAE,OAAO,CAAC,CAAA;IAC/J,MAAM,IAAA,kBAAM,EAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAA;IAC5F,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import { User } from '@things-factory/auth-base'\n\nimport { ActivityInstance } from '../../service/activity-instance/activity-instance.js'\nimport { assign } from './assign.js'\nimport { issue } from './issue.js'\n\nexport interface HandOut {\n /** The activity it is a case of - an application installs it. */\n activityId: string\n /** 「<과정> 수강」, 「성희롱 예방교육 이수 — 2026」 - what the to-do says. */\n name: string\n description?: string\n input?: Record<string, any>\n /** The one it is for. */\n assignee: User\n dueAt?: Date\n}\n\n/**\n * Hands one person a to-do of a module's own - a course to take, a legal training, this week's report - that nobody put\n * up as a request. The case has no creator: it is the person's 할 일 and nobody's 상신함, the one who set it off\n * included when they hand it to themselves (a manager assigning a course to the whole company). Issued as its own\n * assignee, the case went to the person's 상신함 and never to their 할 일 (2026-11-08).\n *\n * `context.state.user` is who set it off - the manager who assigned the course, or the person whose turn came - and\n * stands as the issuer.\n */\nexport async function handOut(todo: HandOut, context: ResolverContext): Promise<ActivityInstance> {\n const { activityId, name, description, input, assignee, dueAt } = todo\n const instance = await issue({ name, description: description ?? name, activityId, input, assignees: [], threadsMin: 0, dueAt, creator: null } as any, context)\n await assign({ id: instance.id, assignees: [assignee.email], dueAt, reason: name }, context)\n return instance\n}\n"]}
1
+ {"version":3,"file":"hand-out.js","sourceRoot":"","sources":["../../../server/controllers/activity-instance/hand-out.ts"],"names":[],"mappings":";;AA+BA,0BAOC;AAnCD,yFAAiF;AACjF,yFAAgF;AAChF,2CAAoC;AACpC,yCAAkC;AAclC;;;;;;;;;;GAUG;AACI,KAAK,UAAU,OAAO,CAAC,IAAa,EAAE,OAAwB;IACnE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IACtE,MAAM,QAAQ,GAAG,MAAM,IAAA,gBAAK,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAS,EAAE,OAAO,CAAC,CAAA;IAC/J,MAAM,IAAA,kBAAM,EAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAA;IAC5F,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,mCAAc,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IACvK,IAAI,MAAM;QAAE,IAAA,kCAAa,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC3E,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import { User } from '@things-factory/auth-base'\n\nimport { ActivityInstance } from '../../service/activity-instance/activity-instance.js'\nimport { ActivityThread } from '../../service/activity-thread/activity-thread.js'\nimport { noticeHandOut } from '../../service/approval-notice/approval-notice.js'\nimport { assign } from './assign.js'\nimport { issue } from './issue.js'\n\nexport interface HandOut {\n /** The activity it is a case of - an application installs it. */\n activityId: string\n /** 「<과정> 수강」, 「성희롱 예방교육 이수 — 2026」 - what the to-do says. */\n name: string\n description?: string\n input?: Record<string, any>\n /** The one it is for. */\n assignee: User\n dueAt?: Date\n}\n\n/**\n * Hands one person a to-do of a module's own - a course to take, a legal training, this week's report - that nobody put\n * up as a request. The case has no creator: it is the person's 할 일 and nobody's 상신함, the one who set it off\n * included when they hand it to themselves (a manager assigning a course to the whole company). Issued as its own\n * assignee, the case went to the person's 상신함 and never to their 할 일 (2026-11-08).\n *\n * `context.state.user` is who set it off - the manager who assigned the course, or the person whose turn came - and\n * stands as the issuer.\n *\n * The person is told (ADR-0200 decision 3) - the to-do opens from the notice - unless they handed it to themselves.\n */\nexport async function handOut(todo: HandOut, context: ResolverContext): Promise<ActivityInstance> {\n const { activityId, name, description, input, assignee, dueAt } = todo\n const instance = await issue({ name, description: description ?? name, activityId, input, assignees: [], threadsMin: 0, dueAt, creator: null } as any, context)\n await assign({ id: instance.id, assignees: [assignee.email], dueAt, reason: name }, context)\n const thread = await context.state.tx.getRepository(ActivityThread).findOne({ where: { activityInstance: { id: instance.id }, assignee: { id: assignee.id } } as any })\n if (thread) noticeHandOut(context, { threadId: thread.id, name, assignee })\n return instance\n}\n"]}
@@ -21,6 +21,7 @@ const activity_approval_js_1 = require("../service/activity-approval/activity-ap
21
21
  const activity_installation_controller_js_1 = require("./activity-installation/activity-installation-controller.js");
22
22
  const assignee_pool_js_1 = require("../service/activity-instance/assignee-pool.js");
23
23
  const assignee_input_js_1 = require("./activity-instance/assignee-input.js");
24
+ const approval_notice_js_1 = require("../service/approval-notice/approval-notice.js");
24
25
  async function updateActivityInstanceState(id, { causedBy }, context) {
25
26
  const { tx } = context.state;
26
27
  var activityInstance = await tx.getRepository(activity_instance_js_1.ActivityInstance).findOne({
@@ -87,6 +88,7 @@ async function closePendingReferences(instanceId, context) {
87
88
  transaction: 'close',
88
89
  terminatedAt: at
89
90
  })));
91
+ await (0, approval_notice_js_1.closeApprovalNotices)(tx, open);
90
92
  }
91
93
  async function evalActivityInstanceState(id, context) {
92
94
  const { tx, user } = context.state;
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../server/controllers/common.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { Role, User } from '@things-factory/auth-base'\n\nimport { Department, Employee, ApprovalLineItem, OrgMemberTargetType } from '@things-factory/organization'\nimport { ActivitySearchKeyItem } from '../service/activity/activity-search-key-item-type.js'\nimport { AssigneeItem, MultipleInstanceType } from '../service/activity/activity.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../service/activity-instance/activity-instance.js'\nimport { ActivityThread, ActivityThreadStatus } from '../service/activity-thread/activity-thread.js'\nimport { ActivityApproval, ActivityApprovalJudgment } from '../service/activity-approval/activity-approval.js'\nimport { ActivityInstallations } from './activity-installation/activity-installation-controller.js'\nimport { poolIdsOf, poolMembers } from '../service/activity-instance/assignee-pool.js'\nimport { ROLE_IS_NOT_AN_ASSIGNEE } from './activity-instance/assignee-input.js'\n\nexport type UpdateActivityInstanceStateAddendum = {\n causedBy?: ActivityThread[]\n}\n\nexport async function updateActivityInstanceState(\n id: string,\n { causedBy }: UpdateActivityInstanceStateAddendum,\n context: ResolverContext\n) {\n const { tx } = context.state\n\n var activityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { 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 activityInstance.transaction = 'thread' /* activity thread에 의해서 변경된 것임을 의미 */\n Object.assign(activityInstance, await evalActivityInstanceState(id, context))\n\n /*\n * **건이 끝나면 참조도 닫는다.**\n *\n * 참조는 결재하지 않으므로 승인·반려로 끝나지 않는다. 닫아 주지 않으면 그 행이 `Pending` 으로 남아\n * 참조 목록에 영원히 나온다 — 오늘 결재자 쪽에서 고친 것과 같은 부류를, 참조를 넣으면서 새로 만들 뻔했다.\n *\n * 중단은 이미 닫힌다(§`activity-thread/_abort.ts` 가 대기 행을 `aborted` 로 닫는다). 여기서 닫는 것은\n * **정상 종료**다.\n */\n if (activityInstance.state === ActivityInstanceStatus.Ended) {\n await closePendingReferences(id, context)\n }\n\n /* callback에서 업무적인 처리도 하고, activityInstance를 일부(주로 input) 수정할 수 있는 기회도 제공. */\n await ActivityInstallations.callback(activityInstance.activity.name, activityInstance, causedBy, context)\n\n await tx.getRepository(ActivityInstance).save(activityInstance)\n}\n\n/**\n * 이 건의 아직 안 닫힌 참조들을 닫는다 — **판정한 것이 아니라 건이 끝난 것이다.**\n *\n * 의견을 냈는지는 `comment` 가 말한다. 비어 있으면 「통지받았고 말하지 않았다」이고, **그것은 동의가\n * 아니다** — 나중에 결재 이력을 읽는 사람이 침묵을 동의로 읽으면 안 된다.\n *\n * `terminator` 를 적지 않는다. 그 사람이 끝낸 것이 아니라 건이 끝난 것이다.\n */\nasync function closePendingReferences(instanceId: string, context: ResolverContext): Promise<void> {\n const { tx } = context.state\n\n const threads = await tx.getRepository(ActivityThread).find({\n where: { activityInstance: { id: instanceId } }\n })\n if (!threads.length) return\n\n const repo = tx.getRepository(ActivityApproval)\n const open = await repo.find({\n where: threads.map(t => ({\n activityThread: { id: t.id },\n participation: 'reference',\n judgment: ActivityApprovalJudgment.Pending\n }))\n })\n if (!open.length) return\n\n const at = new Date()\n await repo.save(\n open.map(row => ({\n ...row,\n judgment: ActivityApprovalJudgment.Closed,\n transaction: 'close',\n terminatedAt: at\n })) as any\n )\n}\n\nexport async function evalActivityInstanceState(\n id: string,\n context: ResolverContext\n): Promise<Partial<ActivityInstance>> {\n const { tx, user } = context.state\n\n const activityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { id },\n relations: ['activityThreads', 'activity']\n })\n const { threadsMin, activityThreads, state, dueAt } = activityInstance\n\n const validThreads = activityThreads.filter(thread => thread.isValid())\n const pendingStartedThreads = activityThreads.filter(thread => thread.isPendingStarted())\n const pendingEndedThreads = activityThreads.filter(thread => thread.isPendingEnded())\n\n switch (true) {\n case state === ActivityInstanceStatus.Draft:\n case state === ActivityInstanceStatus.Ended:\n case state === ActivityInstanceStatus.Aborted:\n return {}\n case validThreads.length === 0:\n return {\n state: ActivityInstanceStatus.Issued\n }\n case threadsMin > validThreads.length:\n return {\n state: ActivityInstanceStatus.Unassigned\n }\n case pendingStartedThreads.length > 0:\n return {\n state: ActivityInstanceStatus.Assigned,\n assignedAt: new Date()\n }\n case pendingEndedThreads.length > 0:\n var started = !dueAt || dueAt.getTime() > Date.now()\n var changed =\n (started && state !== ActivityInstanceStatus.Started) || (!started && state !== ActivityInstanceStatus.Pending)\n\n return !changed\n ? null\n : started\n ? {\n state: ActivityInstanceStatus.Started,\n startedAt: new Date(),\n starter: user\n }\n : {\n state: ActivityInstanceStatus.Pending\n }\n default:\n return {\n state: activityThreads.some(thread => thread.state == ActivityThreadStatus.Aborted)\n ? ActivityInstanceStatus.Aborted\n : ActivityInstanceStatus.Ended,\n reason: '',\n output: [MultipleInstanceType.Parallel, MultipleInstanceType.Sequential].includes(\n activityInstance.activity.multiple\n )\n ? activityThreads\n .filter(thread => thread.state === ActivityThreadStatus.Ended)\n .reduce((sum, thread) => {\n sum[thread.assigneeId] = thread.output\n return sum\n }, {} as any)\n : activityThreads[0].output,\n terminatedAt: new Date(),\n terminator: user\n }\n }\n}\n\nexport async function createActivityThreadWithUnassigned(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread> {\n const result = await saveWaitingThread(transaction, activityInstance, context)\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: [result] }, context)\n\n return result\n}\n\n/**\n * Someone of the pool has just taken a thread of this case - by pick, or by claiming the waiting thread. Is\n * the case still short of people (fewer threads someone holds than `threadsMin`)? Then the rest of the pool\n * is left one waiting thread to take; once it has enough, none.\n *\n * The waiting thread is what puts the case on the pool's to-do lists (my-open-threads.ts). Left behind after\n * the case had its people, it kept the case on the list of everyone in the pool; gone while the case still\n * waited, nobody else in the pool saw it any more.\n *\n * `threads` are the case's threads as they stand once the taker holds theirs, read under the hold on the case.\n * The case's state is not worked out here - the taker does that once, after writing its own thread.\n */\nexport async function leaveTheRestOfThePoolOneWaiting(\n transaction: string,\n activityInstance: ActivityInstance,\n threads: ActivityThread[],\n context: ResolverContext\n): Promise<void> {\n const held = threads.filter(thread => thread.isValid()).length\n const waiting = threads.some(thread => thread.state === ActivityThreadStatus.Unassigned)\n\n if (held < activityInstance.threadsMin && !waiting) {\n await saveWaitingThread(transaction, activityInstance, context)\n }\n}\n\nasync function saveWaitingThread(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n return await threadRepo.save({\n version: 1,\n domain,\n state: ActivityThreadStatus.Unassigned,\n activityInstance,\n transaction,\n output: activityInstance.output,\n dueAt: activityInstance.dueAt,\n assignedAt: now,\n creator: user,\n updater: user,\n createdAt: now\n })\n}\n\nexport async function createActivityThreadForUser(\n transaction: string,\n activityInstance: ActivityInstance,\n assignee: User,\n context: ResolverContext\n): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n const result = await threadRepo.save({\n version: 1,\n domain,\n assignee,\n state: ActivityThreadStatus.Assigned,\n activityInstance,\n transaction,\n output: activityInstance.output,\n dueAt: activityInstance.dueAt,\n assignedAt: now,\n creator: user,\n updater: user,\n createdAt: now\n })\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: [result] }, context)\n\n return result\n}\n\nexport async function createActivityThreadsForUsers(\n transaction: string,\n activityInstance: ActivityInstance,\n assignees: User[],\n context: ResolverContext\n): Promise<ActivityThread[]> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n const distinct = assignees.filter((assignee, idx) => {\n return assignee && !assignees.slice(0, idx).find(x => x.id == assignee.id)\n })\n\n const result = await threadRepo.save(\n distinct.map(assignee => {\n return {\n version: 1,\n assignee,\n state: ActivityThreadStatus.Assigned,\n activityInstance,\n domain,\n transaction,\n output: activityInstance.output,\n creator: user,\n updater: user,\n dueAt: activityInstance.dueAt,\n assignedAt: now\n } as any\n })\n )\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: result }, context)\n\n return result\n}\n\nexport async function createActivityThreadsForAllRoleUsers(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread[]> {\n const { tx } = context.state\n\n /* Everyone the pool reaches - any one of its roles, each person once (assignee-pool.ts). */\n const poolIds = poolIdsOf(activityInstance)\n const roles = poolIds.length ? await tx.getRepository(Role).find({ where: { id: In(poolIds) }, relations: ['users'] }) : []\n const assignees = poolMembers(roles)\n\n if (assignees.length === 0) {\n throw new Error(context.t('error.no user holds the assignee roles', { roles: roles.map(role => role.name).join(', ') }))\n }\n\n // create activity-thread for all of users having given role\n return await createActivityThreadsForUsers(transaction, activityInstance, assignees, context)\n}\n\nexport async function getSystemUserFromMyDepartmentManager(context: ResolverContext): Promise<User> {\n const { domain, user, tx } = context.state\n\n const employee = await tx.getRepository(Employee).findOneBy({\n domain: { id: domain.id },\n user: { id: user.id }\n })\n\n if (!employee || !employee.departmentId) {\n return\n }\n\n const department = await tx.getRepository(Department).findOne({\n where: {\n id: employee.departmentId\n }\n })\n\n if (!department || !department.managerId) return\n\n const manager = await tx.getRepository(Employee).findOne({\n where: {\n domain: { id: domain.id },\n id: department.managerId\n },\n relations: ['user']\n })\n\n return manager?.user\n}\n\nexport async function getSystemUserFromMySupervosor(context: ResolverContext): Promise<User> {\n const { domain, user, tx } = context.state\n\n const employee = await tx.getRepository(Employee).findOneBy({\n domain: { id: domain.id },\n user: { id: user.id }\n })\n\n if (!employee || !employee.supervisorId) {\n return\n }\n\n const supervisor = await tx.getRepository(Employee).findOne({\n where: {\n id: employee.supervisorId\n },\n relations: ['user']\n })\n\n return supervisor?.user\n}\n\nexport async function getSystemUserFromOrgMemberItem(\n item: AssigneeItem | ApprovalLineItem,\n context: ResolverContext\n): Promise<User> {\n const { domain, user, tx } = context.state\n\n const { type, approver, assignee, value } = item as AssigneeItem & ApprovalLineItem\n const id = approver?.id || assignee?.id || value\n\n if (type == OrgMemberTargetType.Employee) {\n /* This domain's only - an id from another tenant made that tenant's person an approver here. */\n const employee =\n id && (await tx.getRepository(Employee).findOne({ where: { domain: { id: domain.id }, id }, relations: ['user'] }))\n\n if (!employee || !employee.user) {\n return\n }\n return employee.user\n } else if (type == OrgMemberTargetType.Department) {\n const department =\n id && (await tx.getRepository(Department).findOne({ where: { domain: { id: domain.id }, id }, relations: ['manager'] }))\n\n if (!department || !department.manager) {\n return\n }\n return await tx.getRepository(User).findOneBy({ id: department.manager.userId })\n } else if (type == OrgMemberTargetType.Role) {\n /*\n * A role is not one person. This answered the role's first user - an unordered relation, so an\n * arbitrary one - and issue() gave that person work meant for the whole role. Each caller decides\n * what a role means there before asking this: the approval line seats the role (create-approval.ts),\n * assignees refuse it (assignee-input.ts). Reaching here is a caller that skipped that step.\n */\n throw Object.assign(new Error(context.t('error.activity-instance a role is not an assignee')), {\n code: ROLE_IS_NOT_AN_ASSIGNEE\n })\n } else if (type == OrgMemberTargetType.Myself) {\n return user\n } else if (type == OrgMemberTargetType.MyDepartment) {\n return await getSystemUserFromMyDepartmentManager(context)\n } else if (type == OrgMemberTargetType.MySupervisor) {\n return await getSystemUserFromMySupervosor(context)\n }\n}\n\nexport function fillActivitySearchKeys(searchKeys: ActivitySearchKeyItem[], input: { [key: string]: any }) {\n if (!input || typeof input !== 'object') {\n return\n }\n\n return searchKeys?.reduce((sum, key, index) => {\n const value = input[key.inputKey]\n if (value != null) {\n sum[`key0${index + 1}`] = value instanceof Array ? value[0] : value\n }\n return sum\n }, {})\n}\n"]}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../server/controllers/common.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { Role, User } from '@things-factory/auth-base'\n\nimport { Department, Employee, ApprovalLineItem, OrgMemberTargetType } from '@things-factory/organization'\nimport { ActivitySearchKeyItem } from '../service/activity/activity-search-key-item-type.js'\nimport { AssigneeItem, MultipleInstanceType } from '../service/activity/activity.js'\nimport { ActivityInstance, ActivityInstanceStatus } from '../service/activity-instance/activity-instance.js'\nimport { ActivityThread, ActivityThreadStatus } from '../service/activity-thread/activity-thread.js'\nimport { ActivityApproval, ActivityApprovalJudgment } from '../service/activity-approval/activity-approval.js'\nimport { ActivityInstallations } from './activity-installation/activity-installation-controller.js'\nimport { poolIdsOf, poolMembers } from '../service/activity-instance/assignee-pool.js'\nimport { ROLE_IS_NOT_AN_ASSIGNEE } from './activity-instance/assignee-input.js'\nimport { closeApprovalNotices } from '../service/approval-notice/approval-notice.js'\n\nexport type UpdateActivityInstanceStateAddendum = {\n causedBy?: ActivityThread[]\n}\n\nexport async function updateActivityInstanceState(\n id: string,\n { causedBy }: UpdateActivityInstanceStateAddendum,\n context: ResolverContext\n) {\n const { tx } = context.state\n\n var activityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { 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 activityInstance.transaction = 'thread' /* activity thread에 의해서 변경된 것임을 의미 */\n Object.assign(activityInstance, await evalActivityInstanceState(id, context))\n\n /*\n * **건이 끝나면 참조도 닫는다.**\n *\n * 참조는 결재하지 않으므로 승인·반려로 끝나지 않는다. 닫아 주지 않으면 그 행이 `Pending` 으로 남아\n * 참조 목록에 영원히 나온다 — 오늘 결재자 쪽에서 고친 것과 같은 부류를, 참조를 넣으면서 새로 만들 뻔했다.\n *\n * 중단은 이미 닫힌다(§`activity-thread/_abort.ts` 가 대기 행을 `aborted` 로 닫는다). 여기서 닫는 것은\n * **정상 종료**다.\n */\n if (activityInstance.state === ActivityInstanceStatus.Ended) {\n await closePendingReferences(id, context)\n }\n\n /* callback에서 업무적인 처리도 하고, activityInstance를 일부(주로 input) 수정할 수 있는 기회도 제공. */\n await ActivityInstallations.callback(activityInstance.activity.name, activityInstance, causedBy, context)\n\n await tx.getRepository(ActivityInstance).save(activityInstance)\n}\n\n/**\n * 이 건의 아직 안 닫힌 참조들을 닫는다 — **판정한 것이 아니라 건이 끝난 것이다.**\n *\n * 의견을 냈는지는 `comment` 가 말한다. 비어 있으면 「통지받았고 말하지 않았다」이고, **그것은 동의가\n * 아니다** — 나중에 결재 이력을 읽는 사람이 침묵을 동의로 읽으면 안 된다.\n *\n * `terminator` 를 적지 않는다. 그 사람이 끝낸 것이 아니라 건이 끝난 것이다.\n */\nasync function closePendingReferences(instanceId: string, context: ResolverContext): Promise<void> {\n const { tx } = context.state\n\n const threads = await tx.getRepository(ActivityThread).find({\n where: { activityInstance: { id: instanceId } }\n })\n if (!threads.length) return\n\n const repo = tx.getRepository(ActivityApproval)\n const open = await repo.find({\n where: threads.map(t => ({\n activityThread: { id: t.id },\n participation: 'reference',\n judgment: ActivityApprovalJudgment.Pending\n }))\n })\n if (!open.length) return\n\n const at = new Date()\n await repo.save(\n open.map(row => ({\n ...row,\n judgment: ActivityApprovalJudgment.Closed,\n transaction: 'close',\n terminatedAt: at\n })) as any\n )\n await closeApprovalNotices(tx, open)\n}\n\nexport async function evalActivityInstanceState(\n id: string,\n context: ResolverContext\n): Promise<Partial<ActivityInstance>> {\n const { tx, user } = context.state\n\n const activityInstance = await tx.getRepository(ActivityInstance).findOne({\n where: { id },\n relations: ['activityThreads', 'activity']\n })\n const { threadsMin, activityThreads, state, dueAt } = activityInstance\n\n const validThreads = activityThreads.filter(thread => thread.isValid())\n const pendingStartedThreads = activityThreads.filter(thread => thread.isPendingStarted())\n const pendingEndedThreads = activityThreads.filter(thread => thread.isPendingEnded())\n\n switch (true) {\n case state === ActivityInstanceStatus.Draft:\n case state === ActivityInstanceStatus.Ended:\n case state === ActivityInstanceStatus.Aborted:\n return {}\n case validThreads.length === 0:\n return {\n state: ActivityInstanceStatus.Issued\n }\n case threadsMin > validThreads.length:\n return {\n state: ActivityInstanceStatus.Unassigned\n }\n case pendingStartedThreads.length > 0:\n return {\n state: ActivityInstanceStatus.Assigned,\n assignedAt: new Date()\n }\n case pendingEndedThreads.length > 0:\n var started = !dueAt || dueAt.getTime() > Date.now()\n var changed =\n (started && state !== ActivityInstanceStatus.Started) || (!started && state !== ActivityInstanceStatus.Pending)\n\n return !changed\n ? null\n : started\n ? {\n state: ActivityInstanceStatus.Started,\n startedAt: new Date(),\n starter: user\n }\n : {\n state: ActivityInstanceStatus.Pending\n }\n default:\n return {\n state: activityThreads.some(thread => thread.state == ActivityThreadStatus.Aborted)\n ? ActivityInstanceStatus.Aborted\n : ActivityInstanceStatus.Ended,\n reason: '',\n output: [MultipleInstanceType.Parallel, MultipleInstanceType.Sequential].includes(\n activityInstance.activity.multiple\n )\n ? activityThreads\n .filter(thread => thread.state === ActivityThreadStatus.Ended)\n .reduce((sum, thread) => {\n sum[thread.assigneeId] = thread.output\n return sum\n }, {} as any)\n : activityThreads[0].output,\n terminatedAt: new Date(),\n terminator: user\n }\n }\n}\n\nexport async function createActivityThreadWithUnassigned(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread> {\n const result = await saveWaitingThread(transaction, activityInstance, context)\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: [result] }, context)\n\n return result\n}\n\n/**\n * Someone of the pool has just taken a thread of this case - by pick, or by claiming the waiting thread. Is\n * the case still short of people (fewer threads someone holds than `threadsMin`)? Then the rest of the pool\n * is left one waiting thread to take; once it has enough, none.\n *\n * The waiting thread is what puts the case on the pool's to-do lists (my-open-threads.ts). Left behind after\n * the case had its people, it kept the case on the list of everyone in the pool; gone while the case still\n * waited, nobody else in the pool saw it any more.\n *\n * `threads` are the case's threads as they stand once the taker holds theirs, read under the hold on the case.\n * The case's state is not worked out here - the taker does that once, after writing its own thread.\n */\nexport async function leaveTheRestOfThePoolOneWaiting(\n transaction: string,\n activityInstance: ActivityInstance,\n threads: ActivityThread[],\n context: ResolverContext\n): Promise<void> {\n const held = threads.filter(thread => thread.isValid()).length\n const waiting = threads.some(thread => thread.state === ActivityThreadStatus.Unassigned)\n\n if (held < activityInstance.threadsMin && !waiting) {\n await saveWaitingThread(transaction, activityInstance, context)\n }\n}\n\nasync function saveWaitingThread(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n return await threadRepo.save({\n version: 1,\n domain,\n state: ActivityThreadStatus.Unassigned,\n activityInstance,\n transaction,\n output: activityInstance.output,\n dueAt: activityInstance.dueAt,\n assignedAt: now,\n creator: user,\n updater: user,\n createdAt: now\n })\n}\n\nexport async function createActivityThreadForUser(\n transaction: string,\n activityInstance: ActivityInstance,\n assignee: User,\n context: ResolverContext\n): Promise<ActivityThread> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n const result = await threadRepo.save({\n version: 1,\n domain,\n assignee,\n state: ActivityThreadStatus.Assigned,\n activityInstance,\n transaction,\n output: activityInstance.output,\n dueAt: activityInstance.dueAt,\n assignedAt: now,\n creator: user,\n updater: user,\n createdAt: now\n })\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: [result] }, context)\n\n return result\n}\n\nexport async function createActivityThreadsForUsers(\n transaction: string,\n activityInstance: ActivityInstance,\n assignees: User[],\n context: ResolverContext\n): Promise<ActivityThread[]> {\n const { domain, user, tx } = context.state\n\n const threadRepo = tx.getRepository(ActivityThread)\n const now = new Date()\n\n const distinct = assignees.filter((assignee, idx) => {\n return assignee && !assignees.slice(0, idx).find(x => x.id == assignee.id)\n })\n\n const result = await threadRepo.save(\n distinct.map(assignee => {\n return {\n version: 1,\n assignee,\n state: ActivityThreadStatus.Assigned,\n activityInstance,\n domain,\n transaction,\n output: activityInstance.output,\n creator: user,\n updater: user,\n dueAt: activityInstance.dueAt,\n assignedAt: now\n } as any\n })\n )\n\n await updateActivityInstanceState(activityInstance.id, { causedBy: result }, context)\n\n return result\n}\n\nexport async function createActivityThreadsForAllRoleUsers(\n transaction: string,\n activityInstance: ActivityInstance,\n context: ResolverContext\n): Promise<ActivityThread[]> {\n const { tx } = context.state\n\n /* Everyone the pool reaches - any one of its roles, each person once (assignee-pool.ts). */\n const poolIds = poolIdsOf(activityInstance)\n const roles = poolIds.length ? await tx.getRepository(Role).find({ where: { id: In(poolIds) }, relations: ['users'] }) : []\n const assignees = poolMembers(roles)\n\n if (assignees.length === 0) {\n throw new Error(context.t('error.no user holds the assignee roles', { roles: roles.map(role => role.name).join(', ') }))\n }\n\n // create activity-thread for all of users having given role\n return await createActivityThreadsForUsers(transaction, activityInstance, assignees, context)\n}\n\nexport async function getSystemUserFromMyDepartmentManager(context: ResolverContext): Promise<User> {\n const { domain, user, tx } = context.state\n\n const employee = await tx.getRepository(Employee).findOneBy({\n domain: { id: domain.id },\n user: { id: user.id }\n })\n\n if (!employee || !employee.departmentId) {\n return\n }\n\n const department = await tx.getRepository(Department).findOne({\n where: {\n id: employee.departmentId\n }\n })\n\n if (!department || !department.managerId) return\n\n const manager = await tx.getRepository(Employee).findOne({\n where: {\n domain: { id: domain.id },\n id: department.managerId\n },\n relations: ['user']\n })\n\n return manager?.user\n}\n\nexport async function getSystemUserFromMySupervosor(context: ResolverContext): Promise<User> {\n const { domain, user, tx } = context.state\n\n const employee = await tx.getRepository(Employee).findOneBy({\n domain: { id: domain.id },\n user: { id: user.id }\n })\n\n if (!employee || !employee.supervisorId) {\n return\n }\n\n const supervisor = await tx.getRepository(Employee).findOne({\n where: {\n id: employee.supervisorId\n },\n relations: ['user']\n })\n\n return supervisor?.user\n}\n\nexport async function getSystemUserFromOrgMemberItem(\n item: AssigneeItem | ApprovalLineItem,\n context: ResolverContext\n): Promise<User> {\n const { domain, user, tx } = context.state\n\n const { type, approver, assignee, value } = item as AssigneeItem & ApprovalLineItem\n const id = approver?.id || assignee?.id || value\n\n if (type == OrgMemberTargetType.Employee) {\n /* This domain's only - an id from another tenant made that tenant's person an approver here. */\n const employee =\n id && (await tx.getRepository(Employee).findOne({ where: { domain: { id: domain.id }, id }, relations: ['user'] }))\n\n if (!employee || !employee.user) {\n return\n }\n return employee.user\n } else if (type == OrgMemberTargetType.Department) {\n const department =\n id && (await tx.getRepository(Department).findOne({ where: { domain: { id: domain.id }, id }, relations: ['manager'] }))\n\n if (!department || !department.manager) {\n return\n }\n return await tx.getRepository(User).findOneBy({ id: department.manager.userId })\n } else if (type == OrgMemberTargetType.Role) {\n /*\n * A role is not one person. This answered the role's first user - an unordered relation, so an\n * arbitrary one - and issue() gave that person work meant for the whole role. Each caller decides\n * what a role means there before asking this: the approval line seats the role (create-approval.ts),\n * assignees refuse it (assignee-input.ts). Reaching here is a caller that skipped that step.\n */\n throw Object.assign(new Error(context.t('error.activity-instance a role is not an assignee')), {\n code: ROLE_IS_NOT_AN_ASSIGNEE\n })\n } else if (type == OrgMemberTargetType.Myself) {\n return user\n } else if (type == OrgMemberTargetType.MyDepartment) {\n return await getSystemUserFromMyDepartmentManager(context)\n } else if (type == OrgMemberTargetType.MySupervisor) {\n return await getSystemUserFromMySupervosor(context)\n }\n}\n\nexport function fillActivitySearchKeys(searchKeys: ActivitySearchKeyItem[], input: { [key: string]: any }) {\n if (!input || typeof input !== 'object') {\n return\n }\n\n return searchKeys?.reduce((sum, key, index) => {\n const value = input[key.inputKey]\n if (value != null) {\n sum[`key0${index + 1}`] = value instanceof Array ? value[0] : value\n }\n return sum\n }, {})\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 ActivityMutation | typeof ActivityQuery)[];
8
+ export declare const resolvers: (typeof ActivityQuery | typeof ActivityMutation)[];
9
9
  export declare const subscribers: (typeof ActivityHistoryEntitySubscriber | typeof ActivityDeclarationGuardSubscriber)[];
@@ -4,5 +4,5 @@ import { ActivityApprovalMutation } from "./activity-approval-mutation.js";
4
4
  import { ActivityApprovalSubscription } from "./activity-approval-subscription.js";
5
5
  import { ActivityApprovalSubscriber } from "./event-subscriber.js";
6
6
  export declare const entities: (typeof ActivityApproval)[];
7
- export declare const resolvers: (typeof ActivityApprovalMutation | typeof ActivityApprovalQuery | typeof ActivityApprovalSubscription)[];
7
+ export declare const resolvers: (typeof ActivityApprovalQuery | typeof ActivityApprovalMutation | typeof ActivityApprovalSubscription)[];
8
8
  export declare const subscribers: (typeof ActivityApprovalSubscriber)[];
@@ -5,5 +5,5 @@ import { ActivityInstanceQuery } from "./activity-instance-query.js";
5
5
  import { ActivityInstanceSubscription } from "./activity-instance-subscription.js";
6
6
  import { ActivityInstanceHistoryEntitySubscriber, ActivityInstanceSubscriber } from "./event-subscriber.js";
7
7
  export declare const entities: (typeof ActivityInstance | typeof ActivityInstanceHistory)[];
8
- export declare const resolvers: (typeof ActivityInstanceMutation | typeof ActivityInstanceQuery | typeof ActivityInstanceSubscription)[];
8
+ export declare const resolvers: (typeof ActivityInstanceQuery | typeof ActivityInstanceMutation | typeof ActivityInstanceSubscription)[];
9
9
  export declare const subscribers: (typeof ActivityInstanceSubscriber | typeof ActivityInstanceHistoryEntitySubscriber)[];
@@ -4,5 +4,5 @@ import { ActivityTemplateMutation } from "./activity-template-mutation.js";
4
4
  import { ActivityTemplateQuery } from "./activity-template-query.js";
5
5
  import { ActivityTemplateHistoryEntitySubscriber } from "./event-subscriber.js";
6
6
  export declare const entities: (typeof ActivityTemplate | typeof ActivityTemplateHistory)[];
7
- export declare const resolvers: (typeof ActivityTemplateMutation | typeof ActivityTemplateQuery)[];
7
+ export declare const resolvers: (typeof ActivityTemplateQuery | typeof ActivityTemplateMutation)[];
8
8
  export declare const subscribers: (typeof ActivityTemplateHistoryEntitySubscriber)[];
@@ -6,5 +6,5 @@ import { ActivityThreadHistoryQuery } from "./activity-thread-history-query.js";
6
6
  import { ActivityThreadSubscription } from "./activity-thread-subscription.js";
7
7
  import { ActivityThreadHistoryEntitySubscriber, ActivityThreadSubscriber } from "./event-subscriber.js";
8
8
  export declare const entities: (typeof ActivityThread | typeof ActivityThreadHistory)[];
9
- export declare const resolvers: (typeof ActivityThreadMutation | typeof ActivityThreadQuery | typeof ActivityThreadHistoryQuery | typeof ActivityThreadSubscription)[];
9
+ export declare const resolvers: (typeof ActivityThreadQuery | typeof ActivityThreadHistoryQuery | typeof ActivityThreadMutation | typeof ActivityThreadSubscription)[];
10
10
  export declare const subscribers: (typeof ActivityThreadSubscriber | typeof ActivityThreadHistoryEntitySubscriber)[];
@@ -65,3 +65,12 @@ export type Translate = (key: string, options?: {
65
65
  * in: no input, no amount, no comment. The page behind the link shows those to whoever may see them.
66
66
  */
67
67
  export declare function noticeWords(t: Translate, facts: NoticeFacts): NoticeWords;
68
+ /** What a hand-out notice tells of - the person's thread of the case. */
69
+ export declare const todoNoticeKey: (threadId: string) => string;
70
+ /** Who is told of a to-do handed out - its person, when they can sign in and did not hand it to themselves. */
71
+ export declare function todoNoticeReceivers(assignee: NoticePerson | null | undefined, actorId?: string | null): NoticePerson[];
72
+ /** The sentences (`todo-notice.*`) - the to-do's name and who handed it, nothing of what it carries. */
73
+ export declare function todoNoticeWords(t: Translate, facts: {
74
+ name: string;
75
+ actor?: string | null;
76
+ }): NoticeWords;
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.APPROVAL_NOTICE_KINDS = void 0;
3
+ exports.todoNoticeKey = exports.APPROVAL_NOTICE_KINDS = void 0;
4
4
  exports.isTurnNotice = isTurnNotice;
5
5
  exports.noticeReceivers = noticeReceivers;
6
6
  exports.noticeAddress = noticeAddress;
7
7
  exports.approvalStep = approvalStep;
8
8
  exports.noticeWords = noticeWords;
9
+ exports.todoNoticeReceivers = todoNoticeReceivers;
10
+ exports.todoNoticeWords = todoNoticeWords;
9
11
  const approval_line_js_1 = require("../../controllers/activity-approval/approval-line.js");
10
12
  exports.APPROVAL_NOTICE_KINDS = ['request', 'delegated', 'reference', 'approved', 'rejected'];
11
13
  /** A notice for the person whose turn it is - the other kinds tell the requester of a judgment. */
@@ -64,4 +66,26 @@ function noticeWords(t, facts) {
64
66
  note: t(isTurnNotice(facts.kind) ? 'approval-notice.see-approval-box' : 'approval-notice.see-request-box', values)
65
67
  };
66
68
  }
69
+ /*
70
+ * A to-do handed out (ADR-0200 decision 3) - a module's own work for one person, nobody's request: a question handed to
71
+ * the person a conversation is about, a legal training, a course. It is told to the person, as a turn is, unless they
72
+ * handed it to themselves; it opens the to-do and is closed when the work is done (`complete`).
73
+ */
74
+ /** What a hand-out notice tells of - the person's thread of the case. */
75
+ const todoNoticeKey = (threadId) => `todo-thread:${threadId}`;
76
+ exports.todoNoticeKey = todoNoticeKey;
77
+ /** Who is told of a to-do handed out - its person, when they can sign in and did not hand it to themselves. */
78
+ function todoNoticeReceivers(assignee, actorId) {
79
+ return assignee?.id && assignee.id !== actorId ? [assignee] : [];
80
+ }
81
+ /** The sentences (`todo-notice.*`) - the to-do's name and who handed it, nothing of what it carries. */
82
+ function todoNoticeWords(t, facts) {
83
+ const values = { name: facts.name, actor: facts.actor || '-' };
84
+ return {
85
+ subject: t('todo-notice.subject', values),
86
+ body: t(facts.actor ? 'todo-notice.body' : 'todo-notice.body-unsaid', values),
87
+ action: t('todo-notice.open', values),
88
+ note: t('approval-notice.see-request-box', values)
89
+ };
90
+ }
67
91
  //# sourceMappingURL=approval-notice-rules.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"approval-notice-rules.js","sourceRoot":"","sources":["../../../server/service/approval-notice/approval-notice-rules.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The rules of the approval notice (ADR-0164) - who is told and what they read. How it is sent is notification's.\n * Plain functions over plain values: no entity, no request, so a test holds every rule without a database.\n */\nimport type { NoticeWords } from '@things-factory/notification'\nimport type { ApprovalLineItem } from '@things-factory/organization'\n\nimport { isApprovalStep } from '../../controllers/activity-approval/approval-line.js'\n\n/**\n * What happened.\n *\n * - `request` a seat's turn came - the first one on submit, the next one after an approval\n * - `delegated` a seat was handed to someone\n * - `reference` someone was seated to read along\n * - `approved` the last approval ended the case\n * - `rejected` an approver sent it back\n */\nexport type ApprovalNoticeKind = 'request' | 'delegated' | 'reference' | 'approved' | 'rejected'\n\nexport const APPROVAL_NOTICE_KINDS: readonly ApprovalNoticeKind[] = ['request', 'delegated', 'reference', 'approved', 'rejected']\n\n/** A notice for the person whose turn it is - the other kinds tell the requester of a judgment. */\nexport function isTurnNotice(kind: ApprovalNoticeKind): boolean {\n return kind === 'request' || kind === 'delegated' || kind === 'reference'\n}\n\nexport type NoticePerson = { id: string; name?: string | null; email?: string | null; locale?: string | null }\n\n/**\n * Who is told.\n *\n * A turn reaches the seat: its person, or - for a seat held by a role - everyone holding the role. A judgment\n * reaches the requester. Whoever did the thing is not told of it, and nobody is told twice.\n */\nexport function noticeReceivers({\n kind,\n approver,\n roleHolders,\n requester,\n actorId\n}: {\n kind: ApprovalNoticeKind\n approver?: NoticePerson | null\n roleHolders?: NoticePerson[] | null\n requester?: NoticePerson | null\n actorId?: string | null\n}): NoticePerson[] {\n const candidates = isTurnNotice(kind) ? (approver ? [approver] : roleHolders || []) : requester ? [requester] : []\n const seen = new Set<string>()\n\n return candidates.filter(person => {\n if (!person?.id || person.id === actorId || seen.has(person.id)) return false\n seen.add(person.id)\n return true\n })\n}\n\n/**\n * The page the notice opens, relative to the domain - the same pages the worklist lists open.\n *\n * A turn opens the approval. An approved case opens the thread's read-only view; a rejected one opens the thread\n * itself, where the requester revises and raises it again.\n */\nexport function noticeAddress(kind: ApprovalNoticeKind, { approvalId, threadId }: { approvalId: string; threadId: string }): string {\n if (isTurnNotice(kind)) return `activity-approval/${approvalId}`\n return kind === 'approved' ? `activity-thread-view/${threadId}` : `activity-thread/${threadId}`\n}\n\n/** Which approval of the line this seat is - references take a place in the line but are not counted. */\nexport function approvalStep(line: ApprovalLineItem[] | null | undefined, order: number): { step: number; steps: number } | undefined {\n const orders = (line || []).map((item, index) => (isApprovalStep(item) ? index + 1 : 0)).filter(Boolean)\n const step = orders.indexOf(order) + 1\n return step ? { step, steps: orders.length } : undefined\n}\n\n\nexport type NoticeFacts = {\n kind: ApprovalNoticeKind\n /** The case as the worklist names it. */\n name: string\n requester?: string | null\n /** Who did the thing - the approver who judged, the person who handed a seat on. */\n actor?: string | null\n step?: number\n steps?: number\n}\n\nexport type Translate = (key: string, options?: { [name: string]: unknown }) => string\n\n/**\n * The sentences, from the worklist translations (`approval-notice.<kind>.*`). Nothing of the request's content goes\n * in: no input, no amount, no comment. The page behind the link shows those to whoever may see them.\n */\nexport function noticeWords(t: Translate, facts: NoticeFacts): NoticeWords {\n const values = {\n name: facts.name,\n requester: facts.requester || '-',\n actor: facts.actor || '-',\n step: facts.step ?? '-',\n steps: facts.steps ?? '-'\n }\n\n return {\n subject: t(`approval-notice.${facts.kind}.subject`, values),\n body: t(`approval-notice.${facts.kind}.body`, values),\n action: t(isTurnNotice(facts.kind) ? 'approval-notice.open-approval' : 'approval-notice.open-request', values),\n note: t(isTurnNotice(facts.kind) ? 'approval-notice.see-approval-box' : 'approval-notice.see-request-box', values)\n }\n}\n"]}
1
+ {"version":3,"file":"approval-notice-rules.js","sourceRoot":"","sources":["../../../server/service/approval-notice/approval-notice-rules.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The rules of the approval notice (ADR-0164) - who is told and what they read. How it is sent is notification's.\n * Plain functions over plain values: no entity, no request, so a test holds every rule without a database.\n */\nimport type { NoticeWords } from '@things-factory/notification'\nimport type { ApprovalLineItem } from '@things-factory/organization'\n\nimport { isApprovalStep } from '../../controllers/activity-approval/approval-line.js'\n\n/**\n * What happened.\n *\n * - `request` a seat's turn came - the first one on submit, the next one after an approval\n * - `delegated` a seat was handed to someone\n * - `reference` someone was seated to read along\n * - `approved` the last approval ended the case\n * - `rejected` an approver sent it back\n */\nexport type ApprovalNoticeKind = 'request' | 'delegated' | 'reference' | 'approved' | 'rejected'\n\nexport const APPROVAL_NOTICE_KINDS: readonly ApprovalNoticeKind[] = ['request', 'delegated', 'reference', 'approved', 'rejected']\n\n/** A notice for the person whose turn it is - the other kinds tell the requester of a judgment. */\nexport function isTurnNotice(kind: ApprovalNoticeKind): boolean {\n return kind === 'request' || kind === 'delegated' || kind === 'reference'\n}\n\nexport type NoticePerson = { id: string; name?: string | null; email?: string | null; locale?: string | null }\n\n/**\n * Who is told.\n *\n * A turn reaches the seat: its person, or - for a seat held by a role - everyone holding the role. A judgment\n * reaches the requester. Whoever did the thing is not told of it, and nobody is told twice.\n */\nexport function noticeReceivers({\n kind,\n approver,\n roleHolders,\n requester,\n actorId\n}: {\n kind: ApprovalNoticeKind\n approver?: NoticePerson | null\n roleHolders?: NoticePerson[] | null\n requester?: NoticePerson | null\n actorId?: string | null\n}): NoticePerson[] {\n const candidates = isTurnNotice(kind) ? (approver ? [approver] : roleHolders || []) : requester ? [requester] : []\n const seen = new Set<string>()\n\n return candidates.filter(person => {\n if (!person?.id || person.id === actorId || seen.has(person.id)) return false\n seen.add(person.id)\n return true\n })\n}\n\n/**\n * The page the notice opens, relative to the domain - the same pages the worklist lists open.\n *\n * A turn opens the approval. An approved case opens the thread's read-only view; a rejected one opens the thread\n * itself, where the requester revises and raises it again.\n */\nexport function noticeAddress(kind: ApprovalNoticeKind, { approvalId, threadId }: { approvalId: string; threadId: string }): string {\n if (isTurnNotice(kind)) return `activity-approval/${approvalId}`\n return kind === 'approved' ? `activity-thread-view/${threadId}` : `activity-thread/${threadId}`\n}\n\n/** Which approval of the line this seat is - references take a place in the line but are not counted. */\nexport function approvalStep(line: ApprovalLineItem[] | null | undefined, order: number): { step: number; steps: number } | undefined {\n const orders = (line || []).map((item, index) => (isApprovalStep(item) ? index + 1 : 0)).filter(Boolean)\n const step = orders.indexOf(order) + 1\n return step ? { step, steps: orders.length } : undefined\n}\n\n\nexport type NoticeFacts = {\n kind: ApprovalNoticeKind\n /** The case as the worklist names it. */\n name: string\n requester?: string | null\n /** Who did the thing - the approver who judged, the person who handed a seat on. */\n actor?: string | null\n step?: number\n steps?: number\n}\n\nexport type Translate = (key: string, options?: { [name: string]: unknown }) => string\n\n/**\n * The sentences, from the worklist translations (`approval-notice.<kind>.*`). Nothing of the request's content goes\n * in: no input, no amount, no comment. The page behind the link shows those to whoever may see them.\n */\nexport function noticeWords(t: Translate, facts: NoticeFacts): NoticeWords {\n const values = {\n name: facts.name,\n requester: facts.requester || '-',\n actor: facts.actor || '-',\n step: facts.step ?? '-',\n steps: facts.steps ?? '-'\n }\n\n return {\n subject: t(`approval-notice.${facts.kind}.subject`, values),\n body: t(`approval-notice.${facts.kind}.body`, values),\n action: t(isTurnNotice(facts.kind) ? 'approval-notice.open-approval' : 'approval-notice.open-request', values),\n note: t(isTurnNotice(facts.kind) ? 'approval-notice.see-approval-box' : 'approval-notice.see-request-box', values)\n }\n}\n\n/*\n * A to-do handed out (ADR-0200 decision 3) - a module's own work for one person, nobody's request: a question handed to\n * the person a conversation is about, a legal training, a course. It is told to the person, as a turn is, unless they\n * handed it to themselves; it opens the to-do and is closed when the work is done (`complete`).\n */\n\n/** What a hand-out notice tells of - the person's thread of the case. */\nexport const todoNoticeKey = (threadId: string) => `todo-thread:${threadId}`\n\n/** Who is told of a to-do handed out - its person, when they can sign in and did not hand it to themselves. */\nexport function todoNoticeReceivers(assignee: NoticePerson | null | undefined, actorId?: string | null): NoticePerson[] {\n return assignee?.id && assignee.id !== actorId ? [assignee] : []\n}\n\n/** The sentences (`todo-notice.*`) - the to-do's name and who handed it, nothing of what it carries. */\nexport function todoNoticeWords(t: Translate, facts: { name: string; actor?: string | null }): NoticeWords {\n const values = { name: facts.name, actor: facts.actor || '-' }\n return {\n subject: t('todo-notice.subject', values),\n body: t(facts.actor ? 'todo-notice.body' : 'todo-notice.body-unsaid', values),\n action: t('todo-notice.open', values),\n note: t('approval-notice.see-request-box', values)\n }\n}\n"]}
@@ -1,3 +1,5 @@
1
+ import { User } from '@things-factory/auth-base';
2
+ import type { EntityManager } from 'typeorm';
1
3
  import { type ApprovalNoticeKind } from './approval-notice-rules.js';
2
4
  /**
3
5
  * Tells of what just happened to approval `approvalId`, once the transaction has committed.
@@ -9,3 +11,23 @@ export declare function noticeApproval(context: ResolverContext, { kind, approva
9
11
  kind: ApprovalNoticeKind;
10
12
  approvalId: string;
11
13
  }): Promise<void>;
14
+ /** What a turn notice tells of - the approval row whose turn it is. */
15
+ export declare const approvalNoticeKey: (approvalId: string) => string;
16
+ /**
17
+ * Closes the turn notices of approval rows that are no longer waiting - judged, handed on, withdrawn or closed with
18
+ * their case (ADR-0164 amendment 1): everyone the turn was told to, a role's holders too, reads it as read and no screen
19
+ * shows it. In the transaction that moved the rows, so a refused move closes nothing.
20
+ */
21
+ export declare function closeApprovalNotices(tx: EntityManager, rows: readonly {
22
+ id: string;
23
+ domainId?: string;
24
+ }[]): Promise<void>;
25
+ /**
26
+ * Tells a person of a to-do handed to them (ADR-0200 decision 3), once the transaction has committed - unless they
27
+ * handed it to themselves. Nothing here throws: the to-do stands without its notice.
28
+ */
29
+ export declare function noticeHandOut(context: ResolverContext, { threadId, name, assignee }: {
30
+ threadId: string;
31
+ name: string;
32
+ assignee: User;
33
+ }): void;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.approvalNoticeKey = void 0;
3
4
  exports.noticeApproval = noticeApproval;
5
+ exports.closeApprovalNotices = closeApprovalNotices;
6
+ exports.noticeHandOut = noticeHandOut;
4
7
  /*
5
8
  * The approval notice (ADR-0164) - the one place the worklist tells people of an approval's turn and judgment.
6
9
  *
@@ -57,6 +60,8 @@ async function noticeApproval(context, { kind, approvalId }) {
57
60
  words: lng => (0, approval_notice_rules_js_1.noticeWords)(translatorOf(lng), facts),
58
61
  address,
59
62
  link: (0, notification_1.noticeLinkOf)(context, address),
63
+ /* a turn is closed once its row is judged (`closeApprovalNotices`); a judgment told is read by its person */
64
+ key: (0, approval_notice_rules_js_1.isTurnNotice)(kind) ? (0, exports.approvalNoticeKey)(approval.id) : `approval-thread:${thread.id}`,
60
65
  label: `approval notice ${kind} of ${approval.id}`
61
66
  }, result => (0, notification_1.keepNoticeDelivery)(activity_approval_js_1.ActivityApproval, approval.id, 'notices', (0, notification_1.noticeDeliveryOf)(kind, result, receivers.length)));
62
67
  }
@@ -64,6 +69,47 @@ async function noticeApproval(context, { kind, approvalId }) {
64
69
  env_1.logger.error(`[worklist] approval notice ${kind} of ${approvalId} could not be made`, error);
65
70
  }
66
71
  }
72
+ /** What a turn notice tells of - the approval row whose turn it is. */
73
+ const approvalNoticeKey = (approvalId) => `approval:${approvalId}`;
74
+ exports.approvalNoticeKey = approvalNoticeKey;
75
+ /**
76
+ * Closes the turn notices of approval rows that are no longer waiting - judged, handed on, withdrawn or closed with
77
+ * their case (ADR-0164 amendment 1): everyone the turn was told to, a role's holders too, reads it as read and no screen
78
+ * shows it. In the transaction that moved the rows, so a refused move closes nothing.
79
+ */
80
+ async function closeApprovalNotices(tx, rows) {
81
+ for (const row of rows) {
82
+ if (!row.domainId)
83
+ continue;
84
+ await (0, notification_1.closeNotices)(tx, { id: row.domainId }, 'approval', (0, exports.approvalNoticeKey)(row.id));
85
+ }
86
+ }
87
+ /**
88
+ * Tells a person of a to-do handed to them (ADR-0200 decision 3), once the transaction has committed - unless they
89
+ * handed it to themselves. Nothing here throws: the to-do stands without its notice.
90
+ */
91
+ function noticeHandOut(context, { threadId, name, assignee }) {
92
+ try {
93
+ const { user } = context.state;
94
+ const receivers = (0, approval_notice_rules_js_1.todoNoticeReceivers)(active(assignee), user?.id);
95
+ if (!receivers.length)
96
+ return;
97
+ const address = `activity-thread/${threadId}`;
98
+ (0, notification_1.sendNoticeWhenCommitted)(context, {
99
+ type: 'todo',
100
+ level: 'info',
101
+ receivers,
102
+ words: lng => (0, approval_notice_rules_js_1.todoNoticeWords)(translatorOf(lng), { name, actor: user?.name }),
103
+ address,
104
+ link: (0, notification_1.noticeLinkOf)(context, address),
105
+ key: (0, approval_notice_rules_js_1.todoNoticeKey)(threadId),
106
+ label: `to-do notice of ${threadId}`
107
+ });
108
+ }
109
+ catch (error) {
110
+ env_1.logger.error(`[worklist] to-do notice of ${threadId} could not be made`, error);
111
+ }
112
+ }
67
113
  /** Only a person who can sign in is told. */
68
114
  function active(person) {
69
115
  return person && person.status === auth_base_1.UserStatus.ACTIVATED ? { id: person.id, name: person.name, email: person.email, locale: person.locale } : null;