@things-factory/worklist 10.1.208 → 10.1.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/components/timeline-entries.js +2 -1
- package/dist-client/components/timeline-entries.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.d.ts +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.d.ts +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.js +36 -12
- package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view-page.js +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
- package/dist-client/pages/todo/draft-list-page.d.ts +1 -2
- package/dist-client/pages/todo/draft-list-page.js +11 -14
- package/dist-client/pages/todo/draft-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/activity-approval/check-authority.d.ts +2 -13
- package/dist-server/controllers/activity-approval/check-authority.js +2 -49
- package/dist-server/controllers/activity-approval/check-authority.js.map +1 -1
- package/dist-server/controllers/activity-approval/delegate.js +1 -8
- package/dist-server/controllers/activity-approval/delegate.js.map +1 -1
- package/dist-server/controllers/activity-approval/delegation.d.ts +5 -15
- package/dist-server/controllers/activity-approval/delegation.js +2 -16
- package/dist-server/controllers/activity-approval/delegation.js.map +1 -1
- package/dist-server/controllers/activity-approval/resolve-judgeable-seats.d.ts +2 -2
- package/dist-server/controllers/activity-approval/resolve-judgeable-seats.js +1 -1
- package/dist-server/controllers/activity-approval/resolve-judgeable-seats.js.map +1 -1
- package/dist-server/controllers/activity-approval/seat-standing.d.ts +66 -0
- package/dist-server/controllers/activity-approval/seat-standing.js +57 -0
- package/dist-server/controllers/activity-approval/seat-standing.js.map +1 -0
- package/dist-server/controllers/activity-instance/issue.js +9 -15
- package/dist-server/controllers/activity-instance/issue.js.map +1 -1
- package/dist-server/controllers/activity-thread/abort.js +7 -0
- package/dist-server/controllers/activity-thread/abort.js.map +1 -1
- package/dist-server/controllers/activity-thread/restart.js +4 -2
- package/dist-server/controllers/activity-thread/restart.js.map +1 -1
- package/dist-server/controllers/activity-thread/submit.js +5 -1
- package/dist-server/controllers/activity-thread/submit.js.map +1 -1
- package/dist-server/controllers/common.js +1 -1
- package/dist-server/controllers/common.js.map +1 -1
- package/dist-server/service/activity/index.d.ts +2 -2
- package/dist-server/service/activity-approval/index.d.ts +1 -1
- package/dist-server/service/activity-approval/my-pending-approvals.d.ts +5 -5
- package/dist-server/service/activity-approval/my-pending-approvals.js +5 -8
- package/dist-server/service/activity-approval/my-pending-approvals.js.map +1 -1
- package/dist-server/service/activity-instance/activity-instance-query.d.ts +1 -1
- package/dist-server/service/activity-instance/activity-instance-query.js +9 -4
- package/dist-server/service/activity-instance/activity-instance-query.js.map +1 -1
- package/dist-server/service/activity-instance/index.d.ts +1 -1
- package/dist-server/service/activity-instance/request-box.d.ts +2 -0
- package/dist-server/service/activity-instance/request-box.js +18 -0
- package/dist-server/service/activity-instance/request-box.js.map +1 -0
- package/dist-server/service/activity-summary/activity-summary-query.js +3 -3
- package/dist-server/service/activity-summary/activity-summary-query.js.map +1 -1
- package/dist-server/service/activity-summary/index.d.ts +1 -1
- package/dist-server/service/activity-template/index.d.ts +2 -2
- package/dist-server/service/activity-thread/index.d.ts +1 -1
- package/dist-server/service/activity-thread/my-open-threads.d.ts +1 -2
- package/dist-server/service/activity-thread/my-open-threads.js +2 -3
- package/dist-server/service/activity-thread/my-open-threads.js.map +1 -1
- package/dist-server/service/index.d.ts +3 -3
- package/dist-server/service/subscription/subscription-filters.d.ts +2 -2
- package/dist-server/service/subscription/subscription-filters.js +2 -2
- package/dist-server/service/subscription/subscription-filters.js.map +1 -1
- package/dist-server/service/work-door/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/shared/task-followup.d.ts +9 -0
- package/shared/task-followup.js +25 -0
- package/tests/delegate-approval.test.ts +7 -4
- package/tests/open-doors.test.ts +1 -1
- package/translations/en.json +4 -3
- package/translations/es.json +4 -3
- package/translations/ja.json +4 -3
- package/translations/ko.json +4 -3
- package/translations/ms.json +4 -3
- package/translations/zh.json +4 -3
- package/dist-server/controllers/activity-approval/own-request.d.ts +0 -162
- package/dist-server/controllers/activity-approval/own-request.js +0 -135
- package/dist-server/controllers/activity-approval/own-request.js.map +0 -1
|
@@ -2,18 +2,7 @@ import { ActivityApproval } from '../../service/activity-approval/activity-appro
|
|
|
2
2
|
/**
|
|
3
3
|
* 이 사람이 이 결재 행을 다룰 수 있나 — approve · reject · abort · delegate · save 가 함께 쓴다.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 결재 행만 받는다. 부르는 다섯이 인스턴스를 싣는 모양이 제각각이라 —
|
|
8
|
-
*
|
|
9
|
-
* reject `checkAuthority` **앞**에서 스레드와 인스턴스를 함께 싣는다
|
|
10
|
-
* approve **뒤**에서 싣는다
|
|
11
|
-
* abort 스레드만 싣는다
|
|
12
|
-
* delegate 스레드만 싣는다
|
|
13
|
-
* save 아예 안 싣는다
|
|
14
|
-
*
|
|
15
|
-
* — 부르는 쪽을 맞추는 길은 **지금 다섯만** 맞추는 것이고 구조가 아니다. 여섯째가 생기면 또 어긋나고,
|
|
16
|
-
* 그때 어긋나는 방식은 「막혀야 할 것이 안 막힌다」다. 그래서 이 함수가 자기에게 필요한 것을 자기가
|
|
17
|
-
* 읽는다 — 이미 역할을 보려고 User 를 한 번 읽고 있다.
|
|
5
|
+
* 그 자리의 결재자이거나, 역할로 걸린 자리면 그 역할을 가진 사람이다. 상신자인지는 묻지 않는다 —
|
|
6
|
+
* 상신자도 결재 자리에 서면 결재한다(2026-10-23 사용자, ADR-0052 ② 를 거둠).
|
|
18
7
|
*/
|
|
19
8
|
export declare function checkAuthority(activityApproval: ActivityApproval, context: ResolverContext): Promise<void>;
|
|
@@ -3,28 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkAuthority = checkAuthority;
|
|
4
4
|
const shell_1 = require("@things-factory/shell");
|
|
5
5
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
6
|
-
const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
|
|
7
|
-
const own_request_js_1 = require("./own-request.js");
|
|
8
6
|
/**
|
|
9
7
|
* 이 사람이 이 결재 행을 다룰 수 있나 — approve · reject · abort · delegate · save 가 함께 쓴다.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* 결재 행만 받는다. 부르는 다섯이 인스턴스를 싣는 모양이 제각각이라 —
|
|
14
|
-
*
|
|
15
|
-
* reject `checkAuthority` **앞**에서 스레드와 인스턴스를 함께 싣는다
|
|
16
|
-
* approve **뒤**에서 싣는다
|
|
17
|
-
* abort 스레드만 싣는다
|
|
18
|
-
* delegate 스레드만 싣는다
|
|
19
|
-
* save 아예 안 싣는다
|
|
20
|
-
*
|
|
21
|
-
* — 부르는 쪽을 맞추는 길은 **지금 다섯만** 맞추는 것이고 구조가 아니다. 여섯째가 생기면 또 어긋나고,
|
|
22
|
-
* 그때 어긋나는 방식은 「막혀야 할 것이 안 막힌다」다. 그래서 이 함수가 자기에게 필요한 것을 자기가
|
|
23
|
-
* 읽는다 — 이미 역할을 보려고 User 를 한 번 읽고 있다.
|
|
9
|
+
* 그 자리의 결재자이거나, 역할로 걸린 자리면 그 역할을 가진 사람이다. 상신자인지는 묻지 않는다 —
|
|
10
|
+
* 상신자도 결재 자리에 서면 결재한다(2026-10-23 사용자, ADR-0052 ② 를 거둠).
|
|
24
11
|
*/
|
|
25
12
|
async function checkAuthority(activityApproval, context) {
|
|
26
13
|
const { user, tx } = context.state;
|
|
27
|
-
await refuseIfTheyRaisedIt(activityApproval, context);
|
|
28
14
|
if (activityApproval.approverId && activityApproval.approverId === user.id) {
|
|
29
15
|
return;
|
|
30
16
|
}
|
|
@@ -40,37 +26,4 @@ async function checkAuthority(activityApproval, context) {
|
|
|
40
26
|
}
|
|
41
27
|
throw new Error(context.t(`error.not authorized to handle this task`));
|
|
42
28
|
}
|
|
43
|
-
/**
|
|
44
|
-
* 상신한 사람이면 거절한다 — **판정하는 자리에서만.**
|
|
45
|
-
*
|
|
46
|
-
* 참조 자리는 막지 않는다. 참조는 판정하지 않으므로 자기가 올린 건에 의견을 남기는 것이 이상하지 않다.
|
|
47
|
-
* `save` 가 이 함수를 지나는 이유가 그 자리다 — 여기서 참조까지 막으면 상신자가 자기 건에 덧붙이는 말이
|
|
48
|
-
* 사라진다.
|
|
49
|
-
*
|
|
50
|
-
* 누구인지 보기 **전에** 선다. 뒤에 두면 역할로 걸린 자리에서 `approver` 를 이 사람으로 채워 놓고 나서
|
|
51
|
-
* 거절하게 되고, 그 한 줄은 트랜잭션이 살아 있는 동안 남는다.
|
|
52
|
-
*/
|
|
53
|
-
async function refuseIfTheyRaisedIt(activityApproval, context) {
|
|
54
|
-
const { user, tx } = context.state;
|
|
55
|
-
if (!(0, own_request_js_1.judges)(activityApproval.participation)) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
/*
|
|
59
|
-
* 스레드를 타고 인스턴스까지 간다. 행이 없으면 막지 않는다 — 여기는 「없는 것」을 판정하는 자리가
|
|
60
|
-
* 아니고, 없다는 사실은 부르는 쪽이 자기 문장으로 말한다.
|
|
61
|
-
*/
|
|
62
|
-
const activityThread = await (0, shell_1.getRepository)(activity_thread_js_1.ActivityThread, tx).findOne({
|
|
63
|
-
where: { id: activityApproval.activityThreadId },
|
|
64
|
-
relations: ['activityInstance']
|
|
65
|
-
});
|
|
66
|
-
/* 판단은 `own-request.ts` 가 한다 — 이 파일은 시험이 못 들어서 값을 가져오는 일만 맡는다. */
|
|
67
|
-
if ((0, own_request_js_1.refuseToJudge)({
|
|
68
|
-
participation: activityApproval.participation,
|
|
69
|
-
issuerId: activityThread?.activityInstance?.issuerId,
|
|
70
|
-
userId: user.id
|
|
71
|
-
})) {
|
|
72
|
-
/* 사람에게는 문장을, 붙잡는 쪽에는 `code` 를 — 커널의 같은 문이 같은 낱말을 던진다. */
|
|
73
|
-
throw (0, own_request_js_1.selfApprovalRefused)(context.t(own_request_js_1.OWN_REQUEST_REFUSAL_KEY));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
29
|
//# sourceMappingURL=check-authority.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-authority.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/check-authority.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"check-authority.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/check-authority.ts"],"names":[],"mappings":";;AAUA,wCAsBC;AAhCD,iDAAqD;AACrD,yDAAgD;AAGhD;;;;;GAKG;AACI,KAAK,UAAU,cAAc,CAAC,gBAAkC,EAAE,OAAwB;IAC/F,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAElC,IAAI,gBAAgB,CAAC,UAAU,IAAI,gBAAgB,CAAC,UAAU,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3E,OAAM;IACR,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,QAAQ,IAAI,gBAAgB,CAAC,cAAc,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,CACZ,MAAM,IAAA,qBAAa,EAAC,gBAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;YACtB,SAAS,EAAE,CAAC,OAAO,CAAC;SACrB,CAAC,CACH,EAAE,KAAK,CAAA;QAER,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,gBAAgB,CAAC,cAAc,CAAC,EAAE,CAAC;YACnE,gBAAgB,CAAC,QAAQ,GAAG,IAAI,CAAA;YAChC,OAAM;QACR,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAA;AACxE,CAAC","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { ActivityApproval } from '../../service/activity-approval/activity-approval.js'\n\n/**\n * 이 사람이 이 결재 행을 다룰 수 있나 — approve · reject · abort · delegate · save 가 함께 쓴다.\n *\n * 그 자리의 결재자이거나, 역할로 걸린 자리면 그 역할을 가진 사람이다. 상신자인지는 묻지 않는다 —\n * 상신자도 결재 자리에 서면 결재한다(2026-10-23 사용자, ADR-0052 ② 를 거둠).\n */\nexport async function checkAuthority(activityApproval: ActivityApproval, context: ResolverContext) {\n const { user, tx } = context.state\n\n if (activityApproval.approverId && activityApproval.approverId === user.id) {\n return\n }\n\n if (!activityApproval.approver && activityApproval.approverRoleId) {\n const roles = (\n await getRepository(User, tx).findOne({\n where: { id: user.id },\n relations: ['roles']\n })\n )?.roles\n\n if (roles.find(role => role.id == activityApproval.approverRoleId)) {\n activityApproval.approver = user\n return\n }\n }\n\n throw new Error(context.t(`error.not authorized to handle this task`))\n}\n"]}
|
|
@@ -7,7 +7,6 @@ const activity_approval_js_1 = require("../../service/activity-approval/activity
|
|
|
7
7
|
const check_authority_js_1 = require("./check-authority.js");
|
|
8
8
|
const claim_js_1 = require("./claim.js");
|
|
9
9
|
const delegation_js_1 = require("./delegation.js");
|
|
10
|
-
const activity_thread_js_1 = require("../../service/activity-thread/activity-thread.js");
|
|
11
10
|
/**
|
|
12
11
|
* 결재를 다른 사람에게 넘긴다.
|
|
13
12
|
*
|
|
@@ -55,13 +54,7 @@ async function delegate({ id, to, comment }, context) {
|
|
|
55
54
|
throw new Error(context.t(`error.user not found`));
|
|
56
55
|
}
|
|
57
56
|
/* 누구에게 넘길 수 있나 — 판단은 `delegation.ts` 가 한다(시험이 그 파일을 든다). */
|
|
58
|
-
const
|
|
59
|
-
where: { domain: { id: domain.id }, id: activityApproval.activityThreadId },
|
|
60
|
-
relations: ['activityInstance']
|
|
61
|
-
});
|
|
62
|
-
const refusal = (0, delegation_js_1.delegationRefusal)({
|
|
63
|
-
participation: activityApproval.participation,
|
|
64
|
-
issuerId: thread?.activityInstance?.issuerId,
|
|
57
|
+
const refusal = (0, delegation_js_1.handOverRefusal)({
|
|
65
58
|
fromUserId: user.id,
|
|
66
59
|
toUserId: approver.id,
|
|
67
60
|
toInDomain: (approver.domains || []).some(one => one.id === domain.id)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/delegate.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"delegate.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/delegate.ts"],"names":[],"mappings":"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","sourcesContent":["import { User, UserStatus } from '@things-factory/auth-base'\nimport { stampedComment } from './opinion.js'\nimport { ObjectRef } from '@things-factory/shell'\nimport { ActivityApproval, ActivityApprovalJudgment } from '../../service/activity-approval/activity-approval.js'\nimport { checkAuthority } from './check-authority.js'\nimport { claimPendingApproval } from './claim.js'\nimport { DELEGATION_REFUSAL_KEYS, handOverRefusal } from './delegation.js'\n\n/**\n * 결재를 다른 사람에게 넘긴다.\n *\n * ── 넘긴 사람의 행은 끝내고, 받은 사람의 행을 새로 만든다 ──────────────────\n * 두 행이 필요하다. 넘긴 기록이 남아야 「누가 왜 넘겼나」에 답할 수 있고, 받은 사람에게 대기 건이\n * 서야 결재가 이어진다.\n *\n * 앞서는 두 번 저장하면서 **둘 다 같은 행을 덮었다.** `{ ...activityApproval }` 이 `id` 까지 퍼뜨려서\n * TypeORM 이 새로 넣지 않고 고쳤기 때문이다. 그래서 위임하면\n *\n * 받은 사람에게 대기 건이 안 생기고\n * 원본은 `delegated` 로 끝나고\n * 스레드는 `submitted` 인 채로 멈춘다\n *\n * **결재가 죽었다.** 오류는 안 났다.\n */\nexport async function delegate(\n { id, to, comment }: { id: string; to: ObjectRef; comment: string },\n context: ResolverContext\n): Promise<ActivityApproval> {\n const { domain, user, tx } = context.state\n\n var repository = tx.getRepository(ActivityApproval)\n\n var activityApproval = await repository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['domain', 'activityThread']\n })\n\n if (!activityApproval) {\n throw new Error(\n context.t('error.activity-approval not found', {\n activityApproval: id\n })\n )\n }\n\n await checkAuthority(activityApproval, context)\n\n /*\n Prerequisite of Delegation\n - Previous judgment should be 'pending'\n */\n if (activityApproval.judgment !== ActivityApprovalJudgment.Pending) {\n throw new Error(\n context.t(`error.activity-approval is already terminated`, {\n activityInstance: id,\n actual: activityApproval.judgment\n })\n )\n }\n\n const approver = await tx.getRepository(User).findOne({\n where: { id: to.id, status: UserStatus.ACTIVATED },\n relations: ['domains']\n })\n\n if (!approver) {\n throw new Error(context.t(`error.user not found`))\n }\n\n /* 누구에게 넘길 수 있나 — 판단은 `delegation.ts` 가 한다(시험이 그 파일을 든다). */\n const refusal = handOverRefusal({\n fromUserId: user.id,\n toUserId: approver.id,\n toInDomain: (approver.domains || []).some(one => one.id === domain.id)\n })\n\n if (refusal) {\n throw Object.assign(new Error(context.t(DELEGATION_REFUSAL_KEYS[refusal])), { code: refusal })\n }\n\n\n /*\n * **여기서 잡는다.** 위의 검사는 읽은 시점의 것이라 그 사이에 남이 처리할 수 있다. 잡기가 첫 쓰기여야\n * 진 쪽이 다음 결재자를 만들기 전에 물러난다.\n */\n if (!(await claimPendingApproval(tx, activityApproval, ActivityApprovalJudgment.Delegated))) {\n /* 「이미 끝났다」보다 **그 사람이 할 일**을 말한다 — 목록을 새로 읽으면 지금 상태가 보인다. */\n throw new Error(context.t('error.activity-approval taken by someone else', { activityApproval: id }))\n }\n\n /*\n * 받은 사람의 대기 건을 **새로 만든다.**\n *\n * `id` 를 퍼뜨리지 않는다 — 퍼뜨리면 원본을 고치는 것이 되고 새 행이 안 생긴다. 차례(`order`)와\n * 차수(`round`)는 같다. 결재선의 같은 단계이고 사람만 바뀐 것이다.\n */\n await repository.save(\n repository.create({\n domain: activityApproval.domain,\n activityThread: activityApproval.activityThread,\n originalApproval: activityApproval,\n round: activityApproval.round,\n order: activityApproval.order,\n /*\n * The seat keeps what it is. Left out, an empty participation reads as approval, so a reference\n * handed on became a judging seat in the receiver's pending list that the line refused to let them\n * approve, and it left their reference list.\n */\n participation: activityApproval.participation,\n judgment: ActivityApprovalJudgment.Pending,\n transaction: 'delegate',\n ...stampedComment(comment),\n approver,\n creator: user,\n updater: user\n })\n )\n\n /* 넘긴 사람의 행을 끝낸다. 의견은 `comment` 에 남긴다 — `reason` 은 이 표에 없는 칸이다. */\n const result = await repository.save({\n ...activityApproval,\n judgment: ActivityApprovalJudgment.Delegated,\n ...stampedComment(comment),\n transaction: 'delegate',\n updater: user,\n terminator: user,\n terminatedAt: new Date()\n })\n\n return result\n}\n"]}
|
|
@@ -7,25 +7,15 @@
|
|
|
7
7
|
* delegate-to-self handing a seat to its own holder changes nothing but the history
|
|
8
8
|
* delegate-outside-domain a person of another tenant would hold a case nobody here can act on,
|
|
9
9
|
* and their inbox, which reads their own domain, would never show it
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* Whoever raised the case may be handed a seat: the one who raises a case may judge it (2026-10-23,
|
|
12
|
+
* ADR-0052 ② withdrawn).
|
|
13
13
|
*/
|
|
14
|
-
export type DelegationRefusal = 'delegate-to-self' | 'delegate-outside-domain'
|
|
14
|
+
export type DelegationRefusal = 'delegate-to-self' | 'delegate-outside-domain';
|
|
15
15
|
export declare const DELEGATION_REFUSAL_KEYS: Record<DelegationRefusal, string>;
|
|
16
|
-
/**
|
|
17
|
-
* The part every hand-over shares - an approval seat or a thread: not to its own holder, and not
|
|
18
|
-
* out of this tenant.
|
|
19
|
-
*/
|
|
16
|
+
/** A hand-over of an approval seat or a thread: not to its own holder, and not out of this tenant. */
|
|
20
17
|
export declare function handOverRefusal({ fromUserId, toUserId, toInDomain }: {
|
|
21
18
|
fromUserId: string;
|
|
22
19
|
toUserId: string;
|
|
23
20
|
toInDomain: boolean;
|
|
24
|
-
}): 'delegate-to-self' | 'delegate-outside-domain' | null;
|
|
25
|
-
export declare function delegationRefusal({ participation, issuerId, fromUserId, toUserId, toInDomain }: {
|
|
26
|
-
participation?: string | null;
|
|
27
|
-
issuerId?: string | null;
|
|
28
|
-
fromUserId: string;
|
|
29
|
-
toUserId: string;
|
|
30
|
-
toInDomain: boolean;
|
|
31
21
|
}): DelegationRefusal | null;
|
|
@@ -2,17 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DELEGATION_REFUSAL_KEYS = void 0;
|
|
4
4
|
exports.handOverRefusal = handOverRefusal;
|
|
5
|
-
exports.delegationRefusal = delegationRefusal;
|
|
6
|
-
const own_request_js_1 = require("./own-request.js");
|
|
7
5
|
exports.DELEGATION_REFUSAL_KEYS = {
|
|
8
6
|
'delegate-to-self': 'error.activity-approval delegate to self',
|
|
9
|
-
'delegate-outside-domain': 'error.activity-approval delegate outside domain'
|
|
10
|
-
'delegate-to-issuer': 'error.activity-approval delegate to issuer'
|
|
7
|
+
'delegate-outside-domain': 'error.activity-approval delegate outside domain'
|
|
11
8
|
};
|
|
12
|
-
/**
|
|
13
|
-
* The part every hand-over shares - an approval seat or a thread: not to its own holder, and not
|
|
14
|
-
* out of this tenant.
|
|
15
|
-
*/
|
|
9
|
+
/** A hand-over of an approval seat or a thread: not to its own holder, and not out of this tenant. */
|
|
16
10
|
function handOverRefusal({ fromUserId, toUserId, toInDomain }) {
|
|
17
11
|
if (!toUserId || toUserId === fromUserId)
|
|
18
12
|
return 'delegate-to-self';
|
|
@@ -20,12 +14,4 @@ function handOverRefusal({ fromUserId, toUserId, toInDomain }) {
|
|
|
20
14
|
return 'delegate-outside-domain';
|
|
21
15
|
return null;
|
|
22
16
|
}
|
|
23
|
-
function delegationRefusal({ participation, issuerId, fromUserId, toUserId, toInDomain }) {
|
|
24
|
-
const shared = handOverRefusal({ fromUserId, toUserId, toInDomain });
|
|
25
|
-
if (shared)
|
|
26
|
-
return shared;
|
|
27
|
-
if ((0, own_request_js_1.judges)(participation) && (0, own_request_js_1.refuseToJudge)({ participation, issuerId, userId: toUserId }))
|
|
28
|
-
return 'delegate-to-issuer';
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
17
|
//# sourceMappingURL=delegation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegation.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/delegation.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"delegation.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/delegation.ts"],"names":[],"mappings":";;;AAqBA,0CAaC;AAnBY,QAAA,uBAAuB,GAAsC;IACxE,kBAAkB,EAAE,0CAA0C;IAC9D,yBAAyB,EAAE,iDAAiD;CAC7E,CAAA;AAED,sGAAsG;AACtG,SAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,QAAQ,EACR,UAAU,EAKX;IACC,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,kBAAkB,CAAA;IACnE,IAAI,CAAC,UAAU;QAAE,OAAO,yBAAyB,CAAA;IAEjD,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["/**\n * May this seat be handed to that person? Kept free of entities so it can be tested.\n *\n * Delegation moves a seat on the approval line from one person to another. It must not change what\n * the seat is, and it must not hand it to someone who cannot sit in it:\n *\n * delegate-to-self handing a seat to its own holder changes nothing but the history\n * delegate-outside-domain a person of another tenant would hold a case nobody here can act on,\n * and their inbox, which reads their own domain, would never show it\n *\n * Whoever raised the case may be handed a seat: the one who raises a case may judge it (2026-10-23,\n * ADR-0052 ② withdrawn).\n */\nexport type DelegationRefusal = 'delegate-to-self' | 'delegate-outside-domain'\n\nexport const DELEGATION_REFUSAL_KEYS: Record<DelegationRefusal, string> = {\n 'delegate-to-self': 'error.activity-approval delegate to self',\n 'delegate-outside-domain': 'error.activity-approval delegate outside domain'\n}\n\n/** A hand-over of an approval seat or a thread: not to its own holder, and not out of this tenant. */\nexport function handOverRefusal({\n fromUserId,\n toUserId,\n toInDomain\n}: {\n fromUserId: string\n toUserId: string\n toInDomain: boolean\n}): DelegationRefusal | null {\n if (!toUserId || toUserId === fromUserId) return 'delegate-to-self'\n if (!toInDomain) return 'delegate-outside-domain'\n\n return null\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ApprovalLineItem } from '@things-factory/organization';
|
|
2
|
-
import type { JudgeableSeat } from './
|
|
2
|
+
import type { JudgeableSeat } from './seat-standing.js';
|
|
3
3
|
/**
|
|
4
4
|
* 결재선을 「자리마다 누가 설 수 있나」로 푼다 — **가져오는 일만 한다.**
|
|
5
5
|
*
|
|
6
|
-
* 판단은 `
|
|
6
|
+
* 판단은 `seat-standing.ts` 의 `judgeSeatsStand` 가 든다. 이 파일은 DB 를 읽어서 시험이 못
|
|
7
7
|
* 드는 쪽이고, 그래서 여기에는 규칙을 한 줄도 두지 않는다.
|
|
8
8
|
*
|
|
9
9
|
* ── 자리를 푸는 규칙은 `create-approval.ts` 를 그대로 따른다 ──────────────
|
|
@@ -9,7 +9,7 @@ const approval_line_standing_js_1 = require("../../service/work-door/approval-li
|
|
|
9
9
|
/**
|
|
10
10
|
* 결재선을 「자리마다 누가 설 수 있나」로 푼다 — **가져오는 일만 한다.**
|
|
11
11
|
*
|
|
12
|
-
* 판단은 `
|
|
12
|
+
* 판단은 `seat-standing.ts` 의 `judgeSeatsStand` 가 든다. 이 파일은 DB 를 읽어서 시험이 못
|
|
13
13
|
* 드는 쪽이고, 그래서 여기에는 규칙을 한 줄도 두지 않는다.
|
|
14
14
|
*
|
|
15
15
|
* ── 자리를 푸는 규칙은 `create-approval.ts` 를 그대로 따른다 ──────────────
|
|
@@ -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 './
|
|
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 './seat-standing.js'\nimport { seatRoleId } from '../../service/work-door/approval-line-standing.js'\n\n/**\n * 결재선을 「자리마다 누가 설 수 있나」로 푼다 — **가져오는 일만 한다.**\n *\n * 판단은 `seat-standing.ts` 의 `judgeSeatsStand` 가 든다. 이 파일은 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"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 결재선의 자리마다 **설 사람이 있나** — 상신하는 순간에 본다.
|
|
3
|
+
*
|
|
4
|
+
* 상신자는 자기 상신의 결재자가 될 수 있다(2026-10-23 사용자: 「상신자는 결재자가 될 수 없다는 원칙을
|
|
5
|
+
* 제거하라」 — ADR-0052 ② 를 거둠). 그래서 여기 남은 물음은 하나다: 결재 자리에 **아무도** 못 서는가.
|
|
6
|
+
*
|
|
7
|
+
* ── 왜 올리는 순간에 보나 ─────────────────────────────────────────────────
|
|
8
|
+
* 결재선의 결재 자리는 차례로 **전부** 판정을 받아야 끝난다. 한 자리만 막혀도 그 건은 거기서 영영
|
|
9
|
+
* 선다. 올릴 때 막으면 **고칠 수 있는 사람이 보는 자리**에서 걸리고, 무엇을 고쳐야 하는지 그 자리에서
|
|
10
|
+
* 말할 수 있다.
|
|
11
|
+
*
|
|
12
|
+
* ── 낱말은 자리를 선언하는 방법을 따른다 ─────────────────────────────────
|
|
13
|
+
* approval-line-missing 결재선 자체가 없다 (§`approval-expectation.ts`)
|
|
14
|
+
* approval-role-empty 지목한 역할에 사람이 0명
|
|
15
|
+
* approval-person-missing 지목한 사람 자리가 안 풀린다
|
|
16
|
+
* 관리자가 할 일이 다르므로 사유를 합치지 않는다. (2026-09-16, 아키텍트 판정)
|
|
17
|
+
*/
|
|
18
|
+
/** 이 자리가 판정하는 자리인가 — 빈 값은 결재다(`ApprovalLineItem.participation` 과 같은 규약). */
|
|
19
|
+
export declare function judges(participation?: string | null): boolean;
|
|
20
|
+
/** 결재 자리 하나와, 거기 설 수 있는 사람들. 역할 자리면 그 역할을 가진 사람 전부. */
|
|
21
|
+
export type JudgeableSeat = {
|
|
22
|
+
/** 사람이 세는 차례 — 1차 결재, 2차 결재. 거절 문구가 이 수를 말한다. */
|
|
23
|
+
order: number;
|
|
24
|
+
/** 역할로 건 자리인가 사람으로 건 자리인가 — **거절 사유가 갈린다.** */
|
|
25
|
+
kind: 'role' | 'person';
|
|
26
|
+
/** 그 자리에 설 수 있는 사람들의 id. 역할 자리면 그 역할을 가진 사람 전부. */
|
|
27
|
+
couldJudge: string[];
|
|
28
|
+
/** 화면에 낼 자리 이름 — 역할 이름이나 사람 이름. */
|
|
29
|
+
name?: string;
|
|
30
|
+
/**
|
|
31
|
+
* 그 자리가 **가리키던 식별자.** 안 풀리는 자리를 고치려면 「누구 자리였는지」를 알아야 한다 —
|
|
32
|
+
* 이름은 못 찾으면 비는데 가리키던 값은 결재선에 그대로 남아 있다.
|
|
33
|
+
*/
|
|
34
|
+
pointsAt?: string;
|
|
35
|
+
};
|
|
36
|
+
/** 거절 사유 하나와, 그 사유로 막힌 자리들. */
|
|
37
|
+
export type SeatRefusal = {
|
|
38
|
+
because: 'approval-role-empty' | 'approval-person-missing';
|
|
39
|
+
seats: {
|
|
40
|
+
order: number;
|
|
41
|
+
name?: string;
|
|
42
|
+
pointsAt?: string;
|
|
43
|
+
}[];
|
|
44
|
+
};
|
|
45
|
+
export type StandingVerdict = {
|
|
46
|
+
ok: true;
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
refusals: SeatRefusal[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* 이 결재선을 올려도 되나 — 결재 자리마다 설 사람이 하나라도 있나.
|
|
53
|
+
*
|
|
54
|
+
* 사유가 둘이면 **둘 다 돌려준다.** 하나씩 말하면 관리자가 하나 고치고 다시 걸린다.
|
|
55
|
+
*/
|
|
56
|
+
export declare function judgeSeatsStand(input: {
|
|
57
|
+
/** **결재 자리만.** 참조는 판정하지 않으므로 여기 오지 않는다. */
|
|
58
|
+
seats: JudgeableSeat[];
|
|
59
|
+
}): StandingVerdict;
|
|
60
|
+
export declare function standingBroken(verdict: StandingVerdict): verdict is Extract<StandingVerdict, {
|
|
61
|
+
ok: false;
|
|
62
|
+
}>;
|
|
63
|
+
/** 사유마다 문구가 다르다 — 관리자가 할 일이 다르기 때문이다. */
|
|
64
|
+
export declare const STANDING_REFUSAL_KEYS: {
|
|
65
|
+
[because in SeatRefusal['because']]: string;
|
|
66
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 결재선의 자리마다 **설 사람이 있나** — 상신하는 순간에 본다.
|
|
4
|
+
*
|
|
5
|
+
* 상신자는 자기 상신의 결재자가 될 수 있다(2026-10-23 사용자: 「상신자는 결재자가 될 수 없다는 원칙을
|
|
6
|
+
* 제거하라」 — ADR-0052 ② 를 거둠). 그래서 여기 남은 물음은 하나다: 결재 자리에 **아무도** 못 서는가.
|
|
7
|
+
*
|
|
8
|
+
* ── 왜 올리는 순간에 보나 ─────────────────────────────────────────────────
|
|
9
|
+
* 결재선의 결재 자리는 차례로 **전부** 판정을 받아야 끝난다. 한 자리만 막혀도 그 건은 거기서 영영
|
|
10
|
+
* 선다. 올릴 때 막으면 **고칠 수 있는 사람이 보는 자리**에서 걸리고, 무엇을 고쳐야 하는지 그 자리에서
|
|
11
|
+
* 말할 수 있다.
|
|
12
|
+
*
|
|
13
|
+
* ── 낱말은 자리를 선언하는 방법을 따른다 ─────────────────────────────────
|
|
14
|
+
* approval-line-missing 결재선 자체가 없다 (§`approval-expectation.ts`)
|
|
15
|
+
* approval-role-empty 지목한 역할에 사람이 0명
|
|
16
|
+
* approval-person-missing 지목한 사람 자리가 안 풀린다
|
|
17
|
+
* 관리자가 할 일이 다르므로 사유를 합치지 않는다. (2026-09-16, 아키텍트 판정)
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.STANDING_REFUSAL_KEYS = void 0;
|
|
21
|
+
exports.judges = judges;
|
|
22
|
+
exports.judgeSeatsStand = judgeSeatsStand;
|
|
23
|
+
exports.standingBroken = standingBroken;
|
|
24
|
+
/** 이 자리가 판정하는 자리인가 — 빈 값은 결재다(`ApprovalLineItem.participation` 과 같은 규약). */
|
|
25
|
+
function judges(participation) {
|
|
26
|
+
return participation !== 'reference';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 이 결재선을 올려도 되나 — 결재 자리마다 설 사람이 하나라도 있나.
|
|
30
|
+
*
|
|
31
|
+
* 사유가 둘이면 **둘 다 돌려준다.** 하나씩 말하면 관리자가 하나 고치고 다시 걸린다.
|
|
32
|
+
*/
|
|
33
|
+
function judgeSeatsStand(input) {
|
|
34
|
+
const { seats } = input;
|
|
35
|
+
const named = (of) => of.map(({ order, name, pointsAt }) => ({ order, name, pointsAt }));
|
|
36
|
+
/* 역할을 지목했는데 그 역할에 사람이 없다. */
|
|
37
|
+
const roleEmpty = seats.filter(seat => seat.kind === 'role' && seat.couldJudge.length === 0);
|
|
38
|
+
/* 지목한 사람이 안 풀린다 — 없어진 직원을 가리키는 자리다. 역할 문제가 아니므로 다른 낱말이다. */
|
|
39
|
+
const personMissing = seats.filter(seat => seat.kind === 'person' && seat.couldJudge.length === 0);
|
|
40
|
+
const refusals = [];
|
|
41
|
+
if (roleEmpty.length) {
|
|
42
|
+
refusals.push({ because: 'approval-role-empty', seats: named(roleEmpty) });
|
|
43
|
+
}
|
|
44
|
+
if (personMissing.length) {
|
|
45
|
+
refusals.push({ because: 'approval-person-missing', seats: named(personMissing) });
|
|
46
|
+
}
|
|
47
|
+
return refusals.length ? { ok: false, refusals } : { ok: true };
|
|
48
|
+
}
|
|
49
|
+
function standingBroken(verdict) {
|
|
50
|
+
return !verdict.ok;
|
|
51
|
+
}
|
|
52
|
+
/** 사유마다 문구가 다르다 — 관리자가 할 일이 다르기 때문이다. */
|
|
53
|
+
exports.STANDING_REFUSAL_KEYS = {
|
|
54
|
+
'approval-role-empty': 'error.approval line names a role that nobody holds',
|
|
55
|
+
'approval-person-missing': 'error.approval line names a person who cannot be found'
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=seat-standing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seat-standing.js","sourceRoot":"","sources":["../../../server/controllers/activity-approval/seat-standing.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAGH,wBAEC;AAgCD,0CAyBC;AAED,wCAEC;AAhED,4EAA4E;AAC5E,SAAgB,MAAM,CAAC,aAA6B;IAClD,OAAO,aAAa,KAAK,WAAW,CAAA;AACtC,CAAC;AA2BD;;;;GAIG;AACH,SAAgB,eAAe,CAAC,KAG/B;IACC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IAEvB,MAAM,KAAK,GAAG,CAAC,EAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAA;IAEzG,6BAA6B;IAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAA;IAE5F,4DAA4D;IAC5D,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAA;IAElG,MAAM,QAAQ,GAAkB,EAAE,CAAA;IAElC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;IAC5E,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACjE,CAAC;AAED,SAAgB,cAAc,CAAC,OAAwB;IACrD,OAAO,CAAC,OAAO,CAAC,EAAE,CAAA;AACpB,CAAC;AAED,yCAAyC;AAC5B,QAAA,qBAAqB,GAAoD;IACpF,qBAAqB,EAAE,oDAAoD;IAC3E,yBAAyB,EAAE,wDAAwD;CACpF,CAAA","sourcesContent":["/**\n * 결재선의 자리마다 **설 사람이 있나** — 상신하는 순간에 본다.\n *\n * 상신자는 자기 상신의 결재자가 될 수 있다(2026-10-23 사용자: 「상신자는 결재자가 될 수 없다는 원칙을\n * 제거하라」 — ADR-0052 ② 를 거둠). 그래서 여기 남은 물음은 하나다: 결재 자리에 **아무도** 못 서는가.\n *\n * ── 왜 올리는 순간에 보나 ─────────────────────────────────────────────────\n * 결재선의 결재 자리는 차례로 **전부** 판정을 받아야 끝난다. 한 자리만 막혀도 그 건은 거기서 영영\n * 선다. 올릴 때 막으면 **고칠 수 있는 사람이 보는 자리**에서 걸리고, 무엇을 고쳐야 하는지 그 자리에서\n * 말할 수 있다.\n *\n * ── 낱말은 자리를 선언하는 방법을 따른다 ─────────────────────────────────\n * approval-line-missing 결재선 자체가 없다 (§`approval-expectation.ts`)\n * approval-role-empty 지목한 역할에 사람이 0명\n * approval-person-missing 지목한 사람 자리가 안 풀린다\n * 관리자가 할 일이 다르므로 사유를 합치지 않는다. (2026-09-16, 아키텍트 판정)\n */\n\n/** 이 자리가 판정하는 자리인가 — 빈 값은 결재다(`ApprovalLineItem.participation` 과 같은 규약). */\nexport function judges(participation?: string | null): boolean {\n return participation !== 'reference'\n}\n\n/** 결재 자리 하나와, 거기 설 수 있는 사람들. 역할 자리면 그 역할을 가진 사람 전부. */\nexport type JudgeableSeat = {\n /** 사람이 세는 차례 — 1차 결재, 2차 결재. 거절 문구가 이 수를 말한다. */\n order: number\n /** 역할로 건 자리인가 사람으로 건 자리인가 — **거절 사유가 갈린다.** */\n kind: 'role' | 'person'\n /** 그 자리에 설 수 있는 사람들의 id. 역할 자리면 그 역할을 가진 사람 전부. */\n couldJudge: string[]\n /** 화면에 낼 자리 이름 — 역할 이름이나 사람 이름. */\n name?: string\n /**\n * 그 자리가 **가리키던 식별자.** 안 풀리는 자리를 고치려면 「누구 자리였는지」를 알아야 한다 —\n * 이름은 못 찾으면 비는데 가리키던 값은 결재선에 그대로 남아 있다.\n */\n pointsAt?: string\n}\n\n/** 거절 사유 하나와, 그 사유로 막힌 자리들. */\nexport type SeatRefusal = {\n because: 'approval-role-empty' | 'approval-person-missing'\n seats: { order: number; name?: string; pointsAt?: string }[]\n}\n\nexport type StandingVerdict = { ok: true } | { ok: false; refusals: SeatRefusal[] }\n\n/**\n * 이 결재선을 올려도 되나 — 결재 자리마다 설 사람이 하나라도 있나.\n *\n * 사유가 둘이면 **둘 다 돌려준다.** 하나씩 말하면 관리자가 하나 고치고 다시 걸린다.\n */\nexport function judgeSeatsStand(input: {\n /** **결재 자리만.** 참조는 판정하지 않으므로 여기 오지 않는다. */\n seats: JudgeableSeat[]\n}): StandingVerdict {\n const { seats } = input\n\n const named = (of: JudgeableSeat[]) => of.map(({ order, name, pointsAt }) => ({ order, name, pointsAt }))\n\n /* 역할을 지목했는데 그 역할에 사람이 없다. */\n const roleEmpty = seats.filter(seat => seat.kind === 'role' && seat.couldJudge.length === 0)\n\n /* 지목한 사람이 안 풀린다 — 없어진 직원을 가리키는 자리다. 역할 문제가 아니므로 다른 낱말이다. */\n const personMissing = seats.filter(seat => seat.kind === 'person' && seat.couldJudge.length === 0)\n\n const refusals: SeatRefusal[] = []\n\n if (roleEmpty.length) {\n refusals.push({ because: 'approval-role-empty', seats: named(roleEmpty) })\n }\n\n if (personMissing.length) {\n refusals.push({ because: 'approval-person-missing', seats: named(personMissing) })\n }\n\n return refusals.length ? { ok: false, refusals } : { ok: true }\n}\n\nexport function standingBroken(verdict: StandingVerdict): verdict is Extract<StandingVerdict, { ok: false }> {\n return !verdict.ok\n}\n\n/** 사유마다 문구가 다르다 — 관리자가 할 일이 다르기 때문이다. */\nexport const STANDING_REFUSAL_KEYS: { [because in SeatRefusal['because']]: string } = {\n 'approval-role-empty': 'error.approval line names a role that nobody holds',\n 'approval-person-missing': 'error.approval line names a person who cannot be found'\n}\n"]}
|
|
@@ -12,7 +12,7 @@ const assignee_pool_js_1 = require("../../service/activity-instance/assignee-poo
|
|
|
12
12
|
const entitlement_js_1 = require("./entitlement.js");
|
|
13
13
|
const claim_draft_js_1 = require("./claim-draft.js");
|
|
14
14
|
const approval_expectation_js_1 = require("./approval-expectation.js");
|
|
15
|
-
const
|
|
15
|
+
const seat_standing_js_1 = require("../activity-approval/seat-standing.js");
|
|
16
16
|
const resolve_judgeable_seats_js_1 = require("../activity-approval/resolve-judgeable-seats.js");
|
|
17
17
|
async function issue(activityInstance, context) {
|
|
18
18
|
const { domain, user, tx } = context.state;
|
|
@@ -157,22 +157,17 @@ async function issue(activityInstance, context) {
|
|
|
157
157
|
}));
|
|
158
158
|
}
|
|
159
159
|
/*
|
|
160
|
-
*
|
|
160
|
+
* **결재 자리마다 설 사람이 있어야 올릴 수 있다.** 아무도 못 서는 자리가 있으면 그 건은 거기서 영영
|
|
161
|
+
* 선다. 올리는 이 자리에서 막아야 결재선을 고칠 수 있는 사람이 무엇을 고쳐야 하는지 읽는다.
|
|
162
|
+
* 상신자도 결재 자리에 설 수 있다(2026-10-23, ADR-0052 ② 를 거둠).
|
|
161
163
|
*
|
|
162
|
-
*
|
|
163
|
-
* 건이 **아무에게도 안 간다.** 정비 완료 확인이 그 모양이면 설비가 정비중으로 남고 라인이 선다.
|
|
164
|
-
*
|
|
165
|
-
* 그래서 올리는 이 자리에서 먼저 본다. 여기서 걸리면 결재선을 고칠 수 있는 사람이 그 자리에서
|
|
166
|
-
* 무엇을 고쳐야 하는지 읽는다. (ADR-0052 ②)
|
|
167
|
-
*
|
|
168
|
-
* 판단은 `own-request.ts` 가 들고, 자리를 푸는 것은 `resolve-judgeable-seats.ts` 가 한다 — 여기에
|
|
164
|
+
* 판단은 `seat-standing.ts` 가 들고, 자리를 푸는 것은 `resolve-judgeable-seats.ts` 가 한다 — 여기에
|
|
169
165
|
* 조건을 적으면 위의 결재선 검사와 같은 이유로 시험할 수 없다.
|
|
170
166
|
*/
|
|
171
|
-
const standing = (0,
|
|
172
|
-
issuerId: user?.id,
|
|
167
|
+
const standing = (0, seat_standing_js_1.judgeSeatsStand)({
|
|
173
168
|
seats: await (0, resolve_judgeable_seats_js_1.resolveJudgeableSeats)(activityInstance.approvalLine ?? origin?.approvalLine, context)
|
|
174
169
|
});
|
|
175
|
-
if ((0,
|
|
170
|
+
if ((0, seat_standing_js_1.standingBroken)(standing)) {
|
|
176
171
|
/*
|
|
177
172
|
* 사유마다 한 문장씩 — **자리가 같다고 사유를 합치지 않는다.** 관리자가 할 일이 다르다: 하나는
|
|
178
173
|
* 「그 자리에 다른 결재자를」이고 하나는 「그 역할에 사람을」이다. (ADR-0047 ② 의 낱말)
|
|
@@ -185,10 +180,9 @@ async function issue(activityInstance, context) {
|
|
|
185
180
|
.join(', ');
|
|
186
181
|
const said = {
|
|
187
182
|
'approval-role-empty': `${seats} 결재 자리가 지목한 역할에 사람이 없습니다. 그대로 올리면 아무도 결재할 수 없습니다 — 그 역할에 사람을 넣어 주십시오.`,
|
|
188
|
-
'approval-person-missing': `${seats} 결재 자리가 지목한 사람을 찾을 수 없습니다. 그대로 올리면 아무도 결재할 수 없습니다 — 그 자리에 지금 있는 사람을 다시 지정해
|
|
189
|
-
'only-the-issuer-could-judge': `${seats} 결재 자리에 올린 사람 말고 설 사람이 없습니다. 그대로 올리면 아무도 결재할 수 없습니다 — 그 자리에 다른 결재자를 넣거나, 그 역할을 가진 사람을 늘려 주십시오.`
|
|
183
|
+
'approval-person-missing': `${seats} 결재 자리가 지목한 사람을 찾을 수 없습니다. 그대로 올리면 아무도 결재할 수 없습니다 — 그 자리에 지금 있는 사람을 다시 지정해 주십시오.`
|
|
190
184
|
}[refusal.because];
|
|
191
|
-
return context.t(
|
|
185
|
+
return context.t(seat_standing_js_1.STANDING_REFUSAL_KEYS[refusal.because], { seats, defaultValue: said });
|
|
192
186
|
})
|
|
193
187
|
.join(' '));
|
|
194
188
|
}
|