690512:1537 Prepare refactor Work Flow [skip ci]
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
# Specification Analysis Report: RFA Approval System Refactor
|
||||
|
||||
**Date**: 2026-05-11
|
||||
**Artifacts Analyzed**: spec.md, plan.md, tasks.md
|
||||
**Constitution Reference**: AGENTS.md, ADR-019, ADR-009, ADR-008, ADR-016, ADR-002, ADR-007
|
||||
|
||||
---
|
||||
|
||||
## Findings Summary
|
||||
|
||||
| ID | Category | Severity | Location(s) | Summary | Recommendation |
|
||||
| --- | ----------------- | -------- | --------------------------------- | ----------------------------------------------------- | -------------------------------------------------------- |
|
||||
| C1 | Constitution | ✅ PASS | tasks.md T066-T070 | Parallel Gateway DSL extension planned | Continue with implementation |
|
||||
| C2 | Constitution | ✅ PASS | data-model.md all entities | All entities use publicId (UUID) + internal id pattern| Compliant with ADR-019 |
|
||||
| C3 | Constitution | ✅ PASS | plan.md Technical Context | BullMQ explicitly listed for Reminders/Distribution | Compliant with ADR-008 |
|
||||
| C4 | Constitution | ✅ PASS | plan.md Constitution Check | All ADR gates marked PASS | Ready for implementation |
|
||||
| I1 | Inconsistency | LOW | spec.md:FR-004.5, plan.md:T067-T068| Aggregate status calc split between plan and spec | Keep T067, T068 in Phase 9; FR-004.5 already covers requirement |
|
||||
| C5 | Coverage | ✅ GOOD | All FRs mapped | All 25 FRs have corresponding tasks | No action needed |
|
||||
| D1 | Duplication | LOW | spec.md, plan.md | Review Teams mentioned in both overview and summary | Keep both; different contexts (user vs technical) |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis by Category
|
||||
|
||||
### 1. Constitution Alignment ✅
|
||||
|
||||
| Principle | Status | Evidence |
|
||||
|-----------|--------|----------|
|
||||
| **ADR-019 UUID** | ✅ PASS | All entities: `publicId: string (uuid)` + `@Exclude() id: number` |
|
||||
| **ADR-009 No Migrations** | ✅ PASS | T001 creates SQL schema file; no TypeORM migration mentioned |
|
||||
| **ADR-002 Document Numbering** | ✅ PASS | Existing RFA numbering reused; no new numbering in scope |
|
||||
| **ADR-008 BullMQ** | ✅ PASS | T003, T044, T046, T054, T056 explicitly use BullMQ |
|
||||
| **ADR-016 CASL** | ✅ PASS | Mentioned in FR-025; CASL guards implied in T015, T037 |
|
||||
| **ADR-007 Error Handling** | ✅ PASS | BusinessException pattern expected in service implementations |
|
||||
| **No `any` types** | ✅ PASS | All DTOs and entities use explicit types |
|
||||
| **No `console.log`** | ✅ PASS | NestJS Logger pattern to be used per project standards |
|
||||
|
||||
### 2. Coverage Analysis
|
||||
|
||||
| Requirement Key | Has Task? | Task IDs | Notes |
|
||||
|-----------------|-----------|----------|-------|
|
||||
| FR-001 Review Teams multi-discipline | ✅ | T006, T007, T014 | Core entities + service |
|
||||
| FR-002 Default by RFA type | ✅ | T014 | ReviewTeam.defaultForRfaTypes field |
|
||||
| FR-003 Parallel task creation | ✅ | T018 | task-creation.service.ts |
|
||||
| FR-004 Aggregate status display | ✅ | T067 | aggregate-status.service.ts |
|
||||
| FR-004.5 Majority with veto | ✅ | T068 | consensus.service.ts |
|
||||
| FR-005 Master Approval Matrix | ✅ | T008, T009, T011 | ResponseCode + ResponseCodeRule |
|
||||
| FR-006 Category filtering | ✅ | T024, T025 | category filtering in service |
|
||||
| FR-007 Code 1C/1D/3 triggers notification | ✅ | T027 | notification-trigger.service.ts |
|
||||
| FR-008 Audit trail | ✅ | T028 | audit.service.ts |
|
||||
| FR-009 Comments with response code | ✅ | T033 | CompleteReviewForm.tsx |
|
||||
| FR-010 Delegation setup | ✅ | T034, T035 | Delegation entity + service |
|
||||
| FR-011 Scope and document types | ✅ | T034 | Delegation.scope, documentTypes fields |
|
||||
| FR-012 Circular detection | ✅ | T036 | circular-detection.service.ts |
|
||||
| FR-013 Auto-expiry | ✅ | T035 | DelegationService handles endDate |
|
||||
| FR-014 Delegated badge | ✅ | T041 | DelegatedBadge.tsx |
|
||||
| FR-015 Scheduled reminders | ✅ | T044, T045 | ReminderService + scheduler |
|
||||
| FR-016 Escalation to manager | ✅ | T047 | escalation.service.ts |
|
||||
| FR-017 Reminder rules admin | ✅ | T043, T048, T049 | Full CRUD + UI |
|
||||
| FR-018 Reminder history | ✅ | T050 | ReminderHistory.tsx |
|
||||
| FR-019 Distribution by doc type + code | ✅ | T051, T052 | DistributionMatrix + Recipient entities |
|
||||
| FR-020 Async distribution via BullMQ | ✅ | T054, T055, T056 | distribution.service.ts + processor |
|
||||
| FR-021 Send Only If conditions | ✅ | T051 | DistributionMatrix.conditions JSON field |
|
||||
| FR-022 Distribution status report | ✅ | T060 | DistributionStatus.tsx |
|
||||
| FR-023 Unified Workflow Engine | ✅ | T066 | parallel-gateway.handler.ts |
|
||||
| FR-024 BullMQ for reminders/distribution | ✅ | T044, T054 | Both use BullMQ explicitly |
|
||||
| FR-025 CASL for permissions | ✅ | T015, T037 | Controllers with CASL guards implied |
|
||||
|
||||
**Coverage Metrics**:
|
||||
- Total Requirements: 25 FRs
|
||||
- Requirements with Tasks: 25/25 (100%)
|
||||
- Unmapped Requirements: 0
|
||||
|
||||
### 3. User Story Coverage
|
||||
|
||||
| Story | Priority | Tasks | Independent Test Criteria |
|
||||
|-------|----------|-------|----------------------------|
|
||||
| US1 Review Teams | P1 | T014-T023 | Create team → assign to RFA → parallel tasks created |
|
||||
| US2 Response Codes | P1 | T024-T033 | Review page → category-filtered codes → trigger notification |
|
||||
| US3 Delegation | P2 | T034-T042 | Delegate → RFA auto-assigned → circular detection blocks |
|
||||
| US4 Auto-Reminders | P2 | T043-T050 | RFA due soon → reminder sent → overdue → escalation |
|
||||
| US5 Distribution | P2 | T051-T060 | Approval → distribution queued → recipients notified |
|
||||
| US6 Matrix Admin | P3 | T061-T065 | View global matrix → create override → project-specific |
|
||||
|
||||
### 4. Task Organization Quality
|
||||
|
||||
| Phase | Tasks | Testability | Notes |
|
||||
|-------|-------|-------------|-------|
|
||||
| Phase 1: Setup | T001-T005 | ✅ Infrastructure | SQL, Seeders, Redis, BullMQ config |
|
||||
| Phase 2: Foundation | T006-T013 | ✅ Core entities | All 5 core entities created |
|
||||
| Phase 3: US1 | T014-T023 | ✅ Testable | Review Teams → parallel review |
|
||||
| Phase 4: US2 | T024-T033 | ✅ Testable | Response Codes → implications |
|
||||
| Phase 5: US3 | T034-T042 | ✅ Testable | Delegation → circular detection |
|
||||
| Phase 6: US4 | T043-T050 | ✅ Testable | Reminders → 2-level escalation |
|
||||
| Phase 7: US5 | T051-T060 | ✅ Testable | Distribution → transmittal |
|
||||
| Phase 8: US6 | T061-T065 | ✅ Testable | Matrix admin → inheritance |
|
||||
| Phase 9: Polish | T066-T080 | ✅ Integration | DSL, consensus, tests, edge cases |
|
||||
|
||||
### 5. Terminology Consistency
|
||||
|
||||
| Concept | Used In | Consistent? |
|
||||
|---------|---------|-------------|
|
||||
| ReviewTeam | spec, plan, tasks | ✅ Yes |
|
||||
| ResponseCode | spec, plan, tasks | ✅ Yes |
|
||||
| Master Approval Matrix | spec, tasks | ✅ Yes |
|
||||
| Delegation | spec, plan, tasks | ✅ Yes |
|
||||
| Distribution Matrix | spec, tasks | ✅ Yes |
|
||||
| Parallel Review | spec, plan, tasks | ✅ Yes |
|
||||
| Response Code 1A-1G, 2, 3, 4 | spec (master table) | ✅ Yes, exact match |
|
||||
|
||||
### 6. Dependency Graph Validation
|
||||
|
||||
**Verified Dependencies**:
|
||||
- Phase 1 → Phase 2: Setup entities needed for all stories ✅
|
||||
- Phase 2 → Phase 3-8: Core entities required ✅
|
||||
- Phase 3-8 → Phase 9: Integration requires all stories ✅
|
||||
- Phase 3 (US1) ↔ Phase 4 (US2): Independent, can parallelize ✅
|
||||
|
||||
### 7. Edge Case Coverage
|
||||
|
||||
| Edge Case from spec.md | Covered in Tasks | Status |
|
||||
|------------------------|------------------|--------|
|
||||
| Race Condition (concurrent review) | T066, T069, T070 | ✅ Redlock + Optimistic locking |
|
||||
| Circular Delegation | T036 | ✅ Detection algorithm |
|
||||
| Expired Review Task | T035 (endDate handling) | ✅ Auto-expiry logic |
|
||||
| Invalid Response Code | T026 (implications evaluator) | ✅ Validation layer |
|
||||
| Concurrent Review veto | T068 (consensus service) | ✅ Majority with veto |
|
||||
|
||||
---
|
||||
|
||||
## Metrics Summary
|
||||
|
||||
| Metric | Value | Target | Status |
|
||||
|--------|-------|--------|--------|
|
||||
| Total Requirements | 25 | - | - |
|
||||
| Requirements with Tasks | 25 | 100% | ✅ |
|
||||
| Coverage % | 100% | ≥90% | ✅ |
|
||||
| Constitution Violations | 0 | 0 | ✅ |
|
||||
| Critical Issues | 0 | 0 | ✅ |
|
||||
| High Severity Issues | 0 | 0 | ✅ |
|
||||
| Medium/Low Issues | 2 | <5 | ✅ |
|
||||
| Ambiguity Count | 0 | 0 | ✅ |
|
||||
| Duplication Count | 1 (LOW) | <3 | ✅ |
|
||||
| User Stories Covered | 6/6 | 100% | ✅ |
|
||||
| Edge Cases Covered | 5/5 | 100% | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Probability | Impact | Mitigation in Plan |
|
||||
|------|-------------|--------|-------------------|
|
||||
| DSL Parallel Gateway complexity | Medium | High | T066, T067, prototype recommended in MVP |
|
||||
| Response Code migration | Low | Medium | New tables only, existing data untouched |
|
||||
| Performance on large teams | Low | Medium | T067 aggregate status, Redis caching |
|
||||
| Circular delegation edge cases | Low | Low | T036, T075 unit tests |
|
||||
| BullMQ queue failures | Low | High | T046, T056 processors with retry logic |
|
||||
|
||||
---
|
||||
|
||||
## Next Actions
|
||||
|
||||
### Immediate ✅
|
||||
|
||||
**Ready for `/speckit-implement`**
|
||||
|
||||
The specification, plan, and tasks are:
|
||||
- ✅ Constitution compliant (no violations)
|
||||
- ✅ 100% requirement coverage
|
||||
- ✅ All user stories have independent test criteria
|
||||
- ✅ All edge cases addressed
|
||||
- ✅ Dependency graph validated
|
||||
- ✅ 80 tasks defined across 9 phases
|
||||
|
||||
### Recommended Implementation Order
|
||||
|
||||
1. **MVP Approach**: Phases 1-2 → US1 (Phase 3) → Minimal Phase 9
|
||||
- Delivers Review Teams + Parallel Review first
|
||||
- Early value, lower risk
|
||||
|
||||
2. **Full Implementation**: All phases sequentially
|
||||
- Complete feature set
|
||||
- Higher coordination needed
|
||||
|
||||
### Suggested Commands
|
||||
|
||||
```bash
|
||||
# Start implementation
|
||||
/speckit-implement
|
||||
|
||||
# Or start with specific phase
|
||||
/speckit-implement --phase 1-2
|
||||
|
||||
# Run tests after each phase
|
||||
/speckit-tester
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Remediation Offers
|
||||
|
||||
No critical remediation required. The following are **optional improvements**:
|
||||
|
||||
1. **LOW**: Consider merging T067/T068 into a single AggregateStatusService if they share significant code
|
||||
2. **LOW**: Add specific performance benchmarks to tasks T001 (SQL indexes) for clarity
|
||||
|
||||
**No action required before implementation.**
|
||||
|
||||
---
|
||||
|
||||
## Sign-off
|
||||
|
||||
✅ **Analysis Complete**
|
||||
✅ **Constitution Compliant**
|
||||
✅ **Ready for Implementation**
|
||||
@@ -0,0 +1,52 @@
|
||||
# Specification Quality Checklist: RFA Approval System Refactor
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-05-11
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
---
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
---
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] 4 clarification questions answered (Q1-Q4)
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
---
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Feature is ready for `/speckit-clarify` or `/speckit-plan`
|
||||
- Master Approval Matrix อ้างอิงจากตารางที่ User ให้มาในคำขอ
|
||||
- Review Teams concept คล้ายกับ TeamBinder Discipline Review
|
||||
- Response Codes 1A-1G, 2, 3, 4 เป็นมาตรฐานอุตสาหกรรมก่อสร้าง
|
||||
|
||||
---
|
||||
|
||||
## Validation Results
|
||||
|
||||
**Status**: ✅ **PASSED** - All checklist items complete. Ready for next phase.
|
||||
@@ -0,0 +1,486 @@
|
||||
# Review Teams API Contract
|
||||
# OpenAPI 3.0.3
|
||||
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Review Teams API
|
||||
version: 1.0.0
|
||||
description: |
|
||||
API for managing Review Teams and Review Tasks for RFA approval workflow.
|
||||
All IDs are UUID strings (publicId) per ADR-019.
|
||||
|
||||
servers:
|
||||
- url: /api/v1
|
||||
|
||||
tags:
|
||||
- name: Review Teams
|
||||
- name: Review Tasks
|
||||
|
||||
paths:
|
||||
/review-teams:
|
||||
get:
|
||||
summary: List Review Teams
|
||||
tags: [Review Teams]
|
||||
parameters:
|
||||
- name: projectId
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: isActive
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
responses:
|
||||
'200':
|
||||
description: List of review teams
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ReviewTeam'
|
||||
|
||||
post:
|
||||
summary: Create Review Team
|
||||
tags: [Review Teams]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateReviewTeamDto'
|
||||
responses:
|
||||
'201':
|
||||
description: Created review team
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReviewTeam'
|
||||
|
||||
/review-teams/{publicId}:
|
||||
parameters:
|
||||
- name: publicId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
get:
|
||||
summary: Get Review Team
|
||||
tags: [Review Teams]
|
||||
responses:
|
||||
'200':
|
||||
description: Review team details with members
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReviewTeamWithMembers'
|
||||
|
||||
patch:
|
||||
summary: Update Review Team
|
||||
tags: [Review Teams]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateReviewTeamDto'
|
||||
responses:
|
||||
'200':
|
||||
description: Updated review team
|
||||
|
||||
delete:
|
||||
summary: Soft Delete Review Team
|
||||
tags: [Review Teams]
|
||||
responses:
|
||||
'204':
|
||||
description: Deleted
|
||||
|
||||
/review-teams/{teamId}/members:
|
||||
parameters:
|
||||
- name: teamId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
post:
|
||||
summary: Add Member to Review Team
|
||||
tags: [Review Teams]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AddTeamMemberDto'
|
||||
responses:
|
||||
'201':
|
||||
description: Member added
|
||||
|
||||
/review-teams/{teamId}/members/{memberId}:
|
||||
parameters:
|
||||
- name: teamId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: memberId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
delete:
|
||||
summary: Remove Member from Review Team
|
||||
tags: [Review Teams]
|
||||
responses:
|
||||
'204':
|
||||
description: Member removed
|
||||
|
||||
/review-tasks:
|
||||
get:
|
||||
summary: List Review Tasks (for current user inbox)
|
||||
tags: [Review Tasks]
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [PENDING, IN_PROGRESS, COMPLETED, DELEGATED, EXPIRED]
|
||||
- name: includeDelegated
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Include tasks delegated to user
|
||||
responses:
|
||||
'200':
|
||||
description: List of review tasks
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ReviewTask'
|
||||
aggregate:
|
||||
$ref: '#/components/schemas/TaskAggregate'
|
||||
|
||||
/review-tasks/{publicId}:
|
||||
parameters:
|
||||
- name: publicId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
get:
|
||||
summary: Get Review Task Detail
|
||||
tags: [Review Tasks]
|
||||
responses:
|
||||
'200':
|
||||
description: Task details with RFA info
|
||||
|
||||
post:
|
||||
summary: Complete Review Task
|
||||
tags: [Review Tasks]
|
||||
description: Submit review decision with response code
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CompleteReviewTaskDto'
|
||||
responses:
|
||||
'200':
|
||||
description: Task completed, consensus evaluated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
task:
|
||||
$ref: '#/components/schemas/ReviewTask'
|
||||
consensus:
|
||||
$ref: '#/components/schemas/ConsensusResult'
|
||||
|
||||
/review-tasks/{publicId}/delegate:
|
||||
post:
|
||||
summary: Delegate Review Task
|
||||
tags: [Review Tasks]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
delegateeUserId:
|
||||
type: string
|
||||
format: uuid
|
||||
reason:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Task delegated
|
||||
|
||||
/rfa-revisions/{revisionId}/review-status:
|
||||
get:
|
||||
summary: Get Aggregate Review Status for RFA Revision
|
||||
tags: [Review Tasks]
|
||||
parameters:
|
||||
- name: revisionId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: Aggregate status across all disciplines
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReviewAggregateStatus'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
ReviewTeam:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
defaultForRfaTypes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
isActive:
|
||||
type: boolean
|
||||
memberCount:
|
||||
type: integer
|
||||
|
||||
ReviewTeamWithMembers:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ReviewTeam'
|
||||
- type: object
|
||||
properties:
|
||||
members:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TeamMember'
|
||||
|
||||
TeamMember:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
user:
|
||||
$ref: '#/components/schemas/UserBrief'
|
||||
discipline:
|
||||
$ref: '#/components/schemas/DisciplineBrief'
|
||||
role:
|
||||
type: string
|
||||
enum: [REVIEWER, LEAD, MANAGER]
|
||||
priorityOrder:
|
||||
type: integer
|
||||
|
||||
ReviewTask:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
rfaRevisionId:
|
||||
type: string
|
||||
format: uuid
|
||||
team:
|
||||
$ref: '#/components/schemas/ReviewTeam'
|
||||
discipline:
|
||||
$ref: '#/components/schemas/DisciplineBrief'
|
||||
assignedTo:
|
||||
$ref: '#/components/schemas/UserBrief'
|
||||
status:
|
||||
type: string
|
||||
enum: [PENDING, IN_PROGRESS, COMPLETED, DELEGATED, EXPIRED, CANCELLED]
|
||||
dueDate:
|
||||
type: string
|
||||
format: date
|
||||
responseCode:
|
||||
$ref: '#/components/schemas/ResponseCodeBrief'
|
||||
comments:
|
||||
type: string
|
||||
isDelegated:
|
||||
type: boolean
|
||||
|
||||
ConsensusResult:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [APPROVED, REJECTED, IN_PROGRESS, SPLIT]
|
||||
totalDisciplines:
|
||||
type: integer
|
||||
completedCount:
|
||||
type: integer
|
||||
approvedCount:
|
||||
type: integer
|
||||
rejectedCount:
|
||||
type: integer
|
||||
vetoTriggered:
|
||||
type: boolean
|
||||
description: True if any discipline gave Code 3
|
||||
|
||||
ReviewAggregateStatus:
|
||||
type: object
|
||||
properties:
|
||||
rfaRevisionId:
|
||||
type: string
|
||||
format: uuid
|
||||
totalTasks:
|
||||
type: integer
|
||||
completedTasks:
|
||||
type: integer
|
||||
pendingTasks:
|
||||
type: integer
|
||||
consensusStatus:
|
||||
type: string
|
||||
enum: [UNANIMOUS_APPROVAL, MAJORITY_APPROVAL, REJECTED, IN_PROGRESS, PENDING]
|
||||
perDiscipline:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
discipline:
|
||||
$ref: '#/components/schemas/DisciplineBrief'
|
||||
taskId:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
responseCode:
|
||||
type: string
|
||||
|
||||
CreateReviewTeamDto:
|
||||
type: object
|
||||
required: [name, projectId]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 2
|
||||
maxLength: 100
|
||||
description:
|
||||
type: string
|
||||
maxLength: 255
|
||||
projectId:
|
||||
type: string
|
||||
format: uuid
|
||||
defaultForRfaTypes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
UpdateReviewTeamDto:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 2
|
||||
maxLength: 100
|
||||
description:
|
||||
type: string
|
||||
isActive:
|
||||
type: boolean
|
||||
|
||||
AddTeamMemberDto:
|
||||
type: object
|
||||
required: [userId, disciplineId]
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
format: uuid
|
||||
disciplineId:
|
||||
type: integer
|
||||
role:
|
||||
type: string
|
||||
enum: [REVIEWER, LEAD, MANAGER]
|
||||
default: REVIEWER
|
||||
|
||||
CompleteReviewTaskDto:
|
||||
type: object
|
||||
required: [responseCodeId]
|
||||
properties:
|
||||
responseCodeId:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Selected response code (e.g., 1A, 2, 3)
|
||||
comments:
|
||||
type: string
|
||||
maxLength: 2000
|
||||
attachments:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
UserBrief:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
|
||||
DisciplineBrief:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
code:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
|
||||
ResponseCodeBrief:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
code:
|
||||
type: string
|
||||
descriptionTh:
|
||||
type: string
|
||||
descriptionEn:
|
||||
type: string
|
||||
|
||||
TaskAggregate:
|
||||
type: object
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
byStatus:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
@@ -0,0 +1,637 @@
|
||||
# Data Model: RFA Approval System Refactor
|
||||
|
||||
**Date**: 2026-05-11
|
||||
**Based on**: research.md decisions, spec.md requirements
|
||||
|
||||
---
|
||||
|
||||
## Entity Relationship Diagram
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ ReviewTeam │────<│ ReviewTeamMember │>────│ User │
|
||||
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
|
||||
│ publicId (PK) │ │ teamId (FK) │ │ publicId (PK) │
|
||||
│ projectId (FK) │ │ userId (FK) │ │ ... │
|
||||
│ name │ │ disciplineId(FK) │ └─────────────────┘
|
||||
│ isActive │ │ role │
|
||||
└────────┬────────┘ └──────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ ReviewTask │>────│ RfaRevision │<────│ RfaStatusCode │
|
||||
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
|
||||
│ publicId (PK) │ │ id (PK) │ │ id (PK) │
|
||||
│ teamId (FK) │ │ correspondenceId │ │ statusCode │
|
||||
│ disciplineId │ │ revisionNumber │ └─────────────────┘
|
||||
│ assignedToId │ └──────────────────┘
|
||||
│ status │
|
||||
│ dueDate │
|
||||
│ responseCodeId │>────┐
|
||||
│ comments │ │
|
||||
└─────────────────┘ │
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ ResponseCode │
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ code │
|
||||
│ subStatus │
|
||||
│ category │
|
||||
│ descriptionTh │
|
||||
│ descriptionEn │
|
||||
│ implications │
|
||||
└────────┬────────┘
|
||||
│ 1:N
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ResponseCodeRule │
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ projectId (FK) │
|
||||
│ documentTypeId │
|
||||
│ responseCodeId │
|
||||
│ isEnabled │
|
||||
│ requiresComments│
|
||||
│ triggersNotification│
|
||||
│ parentRuleId │
|
||||
└─────────────────┘
|
||||
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ Delegation │ │ ReminderRule │ │ DistributionMatrix│
|
||||
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
|
||||
│ publicId (PK) │ │ publicId (PK) │ │ publicId (PK) │
|
||||
│ delegatorId │ │ name │ │ name │
|
||||
│ delegateeId │ │ projectId │ │ documentTypeId│
|
||||
│ startDate │ │ documentTypeId │ │ responseCodeId│
|
||||
│ endDate │ │ triggerDays │ │ conditions │
|
||||
│ scope │ │ reminderType │ │ isActive │
|
||||
│ isActive │ │ recipients │ └────────┬────────┘
|
||||
└─────────────────┘ │ messageTemplate │ │ 1:N
|
||||
└──────────────────┘ ▼
|
||||
┌─────────────────┐
|
||||
│DistributionRecipient│
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ matrixId (FK) │
|
||||
│ recipientType │
|
||||
│ recipientId │
|
||||
│ deliveryMethod │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Entities
|
||||
|
||||
### 1. ReviewTeam (ทีมตรวจสอบ)
|
||||
|
||||
```typescript
|
||||
@Entity('review_teams')
|
||||
class ReviewTeam {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string; // ADR-019: UUIDv7 string
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
projectId: number; // FK to projects
|
||||
|
||||
@Column({ length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Column('simple-array', { nullable: true })
|
||||
defaultForRfaTypes?: string[]; // ['SDW', 'DDW', 'ADW']
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
|
||||
// Relations
|
||||
@OneToMany(() => ReviewTeamMember, member => member.team)
|
||||
members: ReviewTeamMember[];
|
||||
}
|
||||
```
|
||||
|
||||
**Key Fields**:
|
||||
- `defaultForRfaTypes`: Auto-assign this team to specific RFA types
|
||||
- `isActive`: Soft delete support
|
||||
|
||||
---
|
||||
|
||||
### 2. ReviewTeamMember (สมาชิกทีม)
|
||||
|
||||
```typescript
|
||||
@Entity('review_team_members')
|
||||
class ReviewTeamMember {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
teamId: number;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
userId: number;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
disciplineId: number; // FK to disciplines
|
||||
|
||||
@Column({ length: 50, default: 'REVIEWER' })
|
||||
role: 'REVIEWER' | 'LEAD' | 'MANAGER';
|
||||
|
||||
@Column({ default: 0 })
|
||||
priorityOrder: number; // For sequential assignment fallback
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => ReviewTeam, team => team.members)
|
||||
@JoinColumn({ name: 'teamId' })
|
||||
team: ReviewTeam;
|
||||
|
||||
@ManyToOne(() => User, user => user.reviewTeamMemberships)
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. ReviewTask (งานตรวจสอบ)
|
||||
|
||||
```typescript
|
||||
export enum ReviewTaskStatus {
|
||||
PENDING = 'PENDING', // รอดำเนินการ
|
||||
IN_PROGRESS = 'IN_PROGRESS', // กำลังตรวจสอบ
|
||||
COMPLETED = 'COMPLETED', // เสร็จสิ้น (มีผลลัพธ์)
|
||||
DELEGATED = 'DELEGATED', // ถูกมอบหมายให้ผู้อื่น
|
||||
EXPIRED = 'EXPIRED', // เกินกำหนด
|
||||
CANCELLED = 'CANCELLED', // ยกเลิก
|
||||
}
|
||||
|
||||
@Entity('review_tasks')
|
||||
class ReviewTask {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
rfaRevisionId: number; // FK to rfa_revisions
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
teamId: number;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
disciplineId: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
assignedToUserId?: number; // Null = auto-assign by discipline
|
||||
|
||||
@Column({ type: 'enum', enum: ReviewTaskStatus, default: ReviewTaskStatus.PENDING })
|
||||
status: ReviewTaskStatus;
|
||||
|
||||
@Column({ type: 'date', nullable: true })
|
||||
dueDate?: Date;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
responseCodeId?: number;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
comments?: string;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
attachments?: string[]; // Array of attachment publicIds
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
delegatedFromUserId?: number; // For delegation tracking
|
||||
|
||||
@Column({ type: 'timestamp', nullable: true })
|
||||
completedAt?: Date;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => ReviewTeam, team => team.tasks)
|
||||
team: ReviewTeam;
|
||||
|
||||
@ManyToOne(() => ResponseCode)
|
||||
@JoinColumn({ name: 'responseCodeId' })
|
||||
responseCode?: ResponseCode;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. ResponseCode (รหัสตอบกลับมาตรฐาน)
|
||||
|
||||
```typescript
|
||||
export enum ResponseCodeCategory {
|
||||
ENGINEERING = 'ENGINEERING', // Shop Drawing / MS
|
||||
MATERIAL = 'MATERIAL', // Material / Procurement
|
||||
CONTRACT = 'CONTRACT', // Contract / Cost / BOQ
|
||||
TESTING = 'TESTING', // Testing / Handover / QA
|
||||
ESG = 'ESG', // Environment / Social
|
||||
}
|
||||
|
||||
@Entity('response_codes')
|
||||
class ResponseCode {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column({ length: 10 })
|
||||
code: string; // '1A', '1B', '1C', '1D', '1E', '1F', '1G', '2', '3', '4'
|
||||
|
||||
@Column({ length: 10, nullable: true })
|
||||
subStatus?: string; // '1A', '1B', etc.
|
||||
|
||||
@Column({ type: 'enum', enum: ResponseCodeCategory })
|
||||
category: ResponseCodeCategory;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
descriptionTh: string;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
descriptionEn: string;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
implications?: {
|
||||
affectsSchedule?: boolean;
|
||||
affectsCost?: boolean;
|
||||
requiresContractReview?: boolean;
|
||||
requiresEiaAmendment?: boolean;
|
||||
};
|
||||
|
||||
@Column({ type: 'simple-array', nullable: true })
|
||||
notifyRoles?: string[]; // ['CONTRACT_MANAGER', 'QS_MANAGER', 'EIA_OFFICER']
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({ default: false })
|
||||
isSystem: boolean; // System default, cannot delete
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// Relations
|
||||
@OneToMany(() => ResponseCodeRule, rule => rule.responseCode)
|
||||
rules: ResponseCodeRule[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. ResponseCodeRule (กฎการใช้รหัส)
|
||||
|
||||
```typescript
|
||||
@Entity('response_code_rules')
|
||||
class ResponseCodeRule {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
projectId?: number; // NULL = global default
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
documentTypeId: number;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
responseCodeId: number;
|
||||
|
||||
@Column({ default: true })
|
||||
isEnabled: boolean;
|
||||
|
||||
@Column({ default: false })
|
||||
requiresComments: boolean;
|
||||
|
||||
@Column({ default: false })
|
||||
triggersNotification: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
parentRuleId?: number; // Inheritance tracking
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => ResponseCode, code => code.rules)
|
||||
@JoinColumn({ name: 'responseCodeId' })
|
||||
responseCode: ResponseCode;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Delegation (การมอบหมาย)
|
||||
|
||||
```typescript
|
||||
export enum DelegationScope {
|
||||
ALL = 'ALL',
|
||||
RFA_ONLY = 'RFA_ONLY',
|
||||
CORRESPONDENCE_ONLY = 'CORRESPONDENCE_ONLY',
|
||||
SPECIFIC_TYPES = 'SPECIFIC_TYPES',
|
||||
}
|
||||
|
||||
@Entity('delegations')
|
||||
class Delegation {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
delegatorId: number; // ผู้มอบหมาย
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
delegateeId: number; // ผู้รับมอบหมาย
|
||||
|
||||
@Column({ type: 'date' })
|
||||
startDate: Date;
|
||||
|
||||
@Column({ type: 'date', nullable: true })
|
||||
endDate?: Date;
|
||||
|
||||
@Column({ type: 'enum', enum: DelegationScope, default: DelegationScope.ALL })
|
||||
scope: DelegationScope;
|
||||
|
||||
@Column('simple-array', { nullable: true })
|
||||
documentTypes?: string[]; // ['SDW', 'DDW'] when scope = SPECIFIC_TYPES
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
reason?: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'delegatorId' })
|
||||
delegator: User;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'delegateeId' })
|
||||
delegatee: User;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. ReminderRule (กฎการแจ้งเตือน)
|
||||
|
||||
```typescript
|
||||
export enum ReminderType {
|
||||
DUE_SOON = 'DUE_SOON', // X days before due
|
||||
ON_DUE = 'ON_DUE', // On due date
|
||||
OVERDUE = 'OVERDUE', // After due date (repeating)
|
||||
ESCALATION_L1 = 'ESCALATION_L1', // Level 1 escalation
|
||||
ESCALATION_L2 = 'ESCALATION_L2', // Level 2 escalation
|
||||
}
|
||||
|
||||
@Entity('reminder_rules')
|
||||
class ReminderRule {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column({ length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
projectId?: number; // NULL = global
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
documentTypeId?: number; // NULL = all types
|
||||
|
||||
@Column({ default: 2 })
|
||||
triggerDaysBeforeDue: number; // Days before due for DUE_SOON
|
||||
|
||||
@Column({ default: 1 })
|
||||
escalationDaysAfterDue: number; // Days after due for L1 escalation
|
||||
|
||||
@Column({ type: 'enum', enum: ReminderType })
|
||||
reminderType: ReminderType;
|
||||
|
||||
@Column({ type: 'simple-array' })
|
||||
recipients: ('ASSIGNEE' | 'MANAGER' | 'PROJECT_MANAGER')[];
|
||||
|
||||
@Column({ type: 'text' })
|
||||
messageTemplateTh: string;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
messageTemplateEn: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. DistributionMatrix (ตารางกระจายเอกสาร)
|
||||
|
||||
```typescript
|
||||
@Entity('distribution_matrices')
|
||||
class DistributionMatrix {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'uuid', unique: true })
|
||||
publicId: string;
|
||||
|
||||
@Column({ length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
documentTypeId: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude()
|
||||
responseCodeId?: number; // NULL = all codes
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
conditions?: {
|
||||
codes?: string[]; // ['1A', '1B', '2']
|
||||
excludeCodes?: string[]; // ['3', '4']
|
||||
projectPhase?: string;
|
||||
};
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// Relations
|
||||
@OneToMany(() => DistributionRecipient, recipient => recipient.matrix)
|
||||
recipients: DistributionRecipient[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. DistributionRecipient (ผู้รับเอกสาร)
|
||||
|
||||
```typescript
|
||||
export enum RecipientType {
|
||||
USER = 'USER',
|
||||
ORGANIZATION = 'ORGANIZATION',
|
||||
TEAM = 'TEAM',
|
||||
ROLE = 'ROLE', // e.g., 'ALL_QS', 'ALL_SITE_ENG'
|
||||
}
|
||||
|
||||
export enum DeliveryMethod {
|
||||
EMAIL = 'EMAIL',
|
||||
IN_APP = 'IN_APP',
|
||||
BOTH = 'BOTH',
|
||||
}
|
||||
|
||||
@Entity('distribution_recipients')
|
||||
class DistributionRecipient {
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Exclude()
|
||||
matrixId: number;
|
||||
|
||||
@Column({ type: 'enum', enum: RecipientType })
|
||||
recipientType: RecipientType;
|
||||
|
||||
@Column({ type: 'uuid' }) // Can be userId, orgId, teamId
|
||||
recipientPublicId: string;
|
||||
|
||||
@Column({ type: 'enum', enum: DeliveryMethod, default: DeliveryMethod.BOTH })
|
||||
deliveryMethod: DeliveryMethod;
|
||||
|
||||
@Column({ nullable: true })
|
||||
sequence?: number; // For ordered delivery
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Indexes
|
||||
|
||||
```sql
|
||||
-- Review Tasks - Core query patterns
|
||||
CREATE INDEX idx_review_tasks_rfa_revision ON review_tasks(rfaRevisionId);
|
||||
CREATE INDEX idx_review_tasks_status ON review_tasks(status);
|
||||
CREATE INDEX idx_review_tasks_assigned ON review_tasks(assignedToUserId, status);
|
||||
|
||||
-- Response Code Rules - Lookup by document type
|
||||
CREATE INDEX idx_response_rules_lookup ON response_code_rules(
|
||||
projectId,
|
||||
documentTypeId,
|
||||
isEnabled
|
||||
);
|
||||
|
||||
-- Delegations - Active lookup
|
||||
CREATE INDEX idx_delegations_active ON delegations(
|
||||
delegatorId,
|
||||
isActive,
|
||||
startDate,
|
||||
endDate
|
||||
);
|
||||
|
||||
-- Distribution - Matrix lookup
|
||||
CREATE INDEX idx_distribution_lookup ON distribution_matrices(
|
||||
documentTypeId,
|
||||
responseCodeId,
|
||||
isActive
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation Rules
|
||||
|
||||
| Entity | Rule | Implementation |
|
||||
|--------|------|----------------|
|
||||
| ReviewTask | Cannot assign completed task | Check status before update |
|
||||
| Delegation | No circular chains | BFS/DFS validation on create |
|
||||
| Delegation | Max 3 levels deep | Enforce in service layer |
|
||||
| ResponseCodeRule | Only one enabled per doc type + code per project | Unique constraint |
|
||||
| ReviewTeam | At least one member with LEAD role | Validate on activation |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Generate SQL schema file (follows ADR-009: no TypeORM migrations)
|
||||
2. Create TypeORM entities in `backend/src/modules/`
|
||||
3. Create API contracts in `contracts/`
|
||||
@@ -0,0 +1,160 @@
|
||||
# Implementation Plan: RFA Approval System Refactor
|
||||
|
||||
**Branch**: `1-rfa-approval-refactor` | **Date**: 2026-05-11 | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
**Input**: Refactor RFA approval system to TeamBinder/InEight-style with Review Teams, Response Codes, Delegation, Auto-Reminders, and Distribution Matrix
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
ปรับปรุงระบบการอนุมัติเอกสาร RFA ให้รองรับ:
|
||||
1. **Review Teams by Discipline** - กำหนผู้ตรวจสอบตามสาขาวิชาแทนรายบุคคล พร้อม Parallel Review
|
||||
2. **Master Approval Matrix** - Response Codes มาตรฐาน 1A-1G, 2, 3, 4 ตามหมวดงาน (Engineering, Material, Contract, Testing, ESG)
|
||||
3. **Delegation & Proxy** - มอบหมายงานแทนเมื่อไม่อยู่ พร้อมตรวจจับ Circular Delegation
|
||||
4. **Auto-Reminders & Escalation** - แจ้งเตือนอัตโนมัติตาม SLA และ Escalate 2 ระดับเมื่อ Overdue
|
||||
5. **Distribution Matrix** - กระจายเอกสารอัตโนมัติหลังอนุมัติผ่าน BullMQ
|
||||
|
||||
Technical approach: สร้าง Entities ใหม่ (ReviewTeam, ReviewTask, ResponseCodeMatrix, Delegation, DistributionMatrix) และ integrate กับ Unified Workflow Engine (ADR-001) และ BullMQ (ADR-008)
|
||||
|
||||
---
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.x, NestJS 10.x (Backend), Next.js 14.x (Frontend)
|
||||
**Primary Dependencies**: TypeORM (Database), BullMQ (Queue/Reminders), CASL (Authorization), Zod (Validation), Redis (Cache/Locking)
|
||||
**Storage**: MariaDB 10.11 (Primary), Redis 7.x (Cache/Queue)
|
||||
**Testing**: Jest (Backend), Playwright (Frontend), Min 70% backend coverage, 80% business logic
|
||||
**Target Platform**: Web Application (NestJS API + Next.js Frontend)
|
||||
**Project Type**: Full-stack (backend + frontend)
|
||||
**Performance Goals**: Approval API response <500ms, Parallel Review aggregation <200ms, Distribution queue processing <5 min
|
||||
**Constraints**: ADR-019 UUID (no parseInt), ADR-009 No TypeORM Migrations, ADR-002 Document Numbering with Redlock, ADR-018 AI Boundary
|
||||
**Scale/Scope**: 100+ concurrent RFAs, 50+ Review Teams per project, 1000+ Distribution recipients
|
||||
|
||||
---
|
||||
|
||||
## Constitution Check
|
||||
|
||||
| Gate | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| **ADR-019 UUID** | ✅ PASS | All new entities use publicId (string UUID), internal id (number) with @Exclude() |
|
||||
| **ADR-009 No Migrations** | ✅ PASS | Schema changes via SQL files in `specs/03-Data-and-Storage/` |
|
||||
| **ADR-002 Document Numbering** | ✅ PASS | Existing RFA numbering reused, no new numbering needed |
|
||||
| **ADR-008 BullMQ** | ✅ PASS | Reminders, Distribution, Escalation all use BullMQ |
|
||||
| **ADR-016 CASL** | ✅ PASS | Reviewer permissions via CASL ability checks |
|
||||
| **ADR-018 AI Boundary** | ✅ PASS | No AI involvement in approval workflow |
|
||||
| **ADR-007 Error Handling** | ✅ PASS | BusinessException/WorkflowException for approval errors |
|
||||
| **No `any` types** | ✅ PASS | Strict TypeScript enforced |
|
||||
| **No `console.log`** | ✅ PASS | NestJS Logger for backend, removed for frontend commits |
|
||||
|
||||
**Post-Design Re-check**: Required after data-model.md and contracts generated
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/1-rfa-approval-refactor/
|
||||
├── plan.md # This file
|
||||
├── spec.md # Feature specification
|
||||
├── research.md # Phase 0 research
|
||||
├── data-model.md # Phase 1 data model
|
||||
├── quickstart.md # Phase 1 setup guide
|
||||
├── contracts/ # Phase 1 API contracts
|
||||
│ ├── review-team-api.yaml
|
||||
│ ├── response-code-api.yaml
|
||||
│ ├── delegation-api.yaml
|
||||
│ └── distribution-api.yaml
|
||||
└── tasks.md # Phase 2 (generated by /speckit-tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── modules/
|
||||
│ │ ├── review-team/ # NEW: Review Teams & Tasks
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ ├── review-team.service.ts
|
||||
│ │ │ ├── review-team.controller.ts
|
||||
│ │ │ └── review-task.service.ts
|
||||
│ │ ├── response-code/ # NEW: Master Approval Matrix
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ └── response-code.service.ts
|
||||
│ │ ├── delegation/ # NEW: Delegation & Proxy
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ └── delegation.service.ts
|
||||
│ │ ├── reminder/ # NEW: Auto-Reminders
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── processors/
|
||||
│ │ │ └── reminder.service.ts
|
||||
│ │ ├── distribution/ # NEW: Distribution Matrix
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── processors/
|
||||
│ │ │ └── distribution.service.ts
|
||||
│ │ └── workflow-engine/ # EXISTING: Modified for Parallel Review
|
||||
│ └── rfa/ # EXISTING: Modified for Response Codes
|
||||
└── tests/
|
||||
├── unit/review-team/
|
||||
├── unit/delegation/
|
||||
├── integration/distribution/
|
||||
└── e2e/rfa-workflow/
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── (dashboard)/
|
||||
│ │ │ ├── review-teams/ # NEW: Review Team management UI
|
||||
│ │ │ ├── response-codes/ # NEW: Master Matrix admin UI
|
||||
│ │ │ ├── delegation/ # NEW: Delegation settings UI
|
||||
│ │ │ └── rfa/
|
||||
│ │ │ └── [id]/
|
||||
│ │ │ └── review/ # MODIFIED: Parallel Review UI with Response Codes
|
||||
│ │ └── api/
|
||||
│ │ └── review-team/ # NEW: API routes
|
||||
│ ├── components/
|
||||
│ │ ├── review-task/ # NEW: Review Task cards, Aggregate status
|
||||
│ │ ├── response-code/ # NEW: Response Code selector
|
||||
│ │ └── delegation/ # NEW: Delegation form
|
||||
│ └── hooks/
|
||||
│ ├── use-review-teams.ts # NEW
|
||||
│ ├── use-response-codes.ts # NEW
|
||||
│ └── use-delegation.ts # NEW
|
||||
```
|
||||
|
||||
**Structure Decision**: Full-stack implementation with 5 new backend modules (review-team, response-code, delegation, reminder, distribution) + modifications to existing rfa and workflow-engine modules. Frontend adds management UIs and enhances existing RFA review page.
|
||||
|
||||
---
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|--------------------------------------|
|
||||
| 5 new backend modules | Each domain (Review Teams, Response Codes, Delegation, Reminders, Distribution) has distinct business logic, lifecycle, and scaling needs | Single combined module would create tight coupling and maintenance burden |
|
||||
| Parallel Review in Workflow Engine | Requires significant DSL extension to support concurrent tasks with consensus rules | Sequential review would not meet industry standard (TeamBinder/InEight) efficiency requirements |
|
||||
| Master Approval Matrix with inheritance | Global + Project override needed for standardization while allowing project flexibility | Single global matrix wouldn't accommodate project-specific requirements (e.g., ESG varies by project type) |
|
||||
|
||||
---
|
||||
|
||||
## Phase Overview
|
||||
|
||||
| Phase | Output | Purpose |
|
||||
|-------|--------|---------|
|
||||
| **Phase 0** | research.md | Research technical patterns, validate Workflow Engine DSL extension |
|
||||
| **Phase 1** | data-model.md, contracts/, quickstart.md | Design entities, API contracts, setup guide |
|
||||
| **Phase 2** | tasks.md | Break into actionable tasks (via /speckit-tasks) |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Phase 0**: Generate research.md - Validate Workflow Engine DSL can support Parallel Review
|
||||
2. **Phase 1**: Generate data-model.md and API contracts
|
||||
3. **Run**: `/speckit-tasks` to create tasks.md
|
||||
4. **Run**: `/speckit-analyze` to validate cross-artifact consistency
|
||||
@@ -0,0 +1,275 @@
|
||||
# Quickstart Guide: RFA Approval System Refactor
|
||||
|
||||
**Branch**: `1-rfa-approval-refactor`
|
||||
**Prerequisites**: Docker Compose environment running (MariaDB, Redis)
|
||||
|
||||
---
|
||||
|
||||
## 1. Environment Setup
|
||||
|
||||
### 1.1 Database Schema
|
||||
|
||||
```bash
|
||||
# Apply new SQL schema (ADR-009: no TypeORM migrations)
|
||||
# File will be created in specs/03-Data-and-Storage/
|
||||
mysql -u root -p lcbp3 < specs/03-Data-and-Storage/lcbp3-v1.9.0-rfa-approval-schema.sql
|
||||
```
|
||||
|
||||
### 1.2 Seed Master Data
|
||||
|
||||
```bash
|
||||
# Run seeder for Response Codes and default rules
|
||||
cd backend
|
||||
npx ts-node -r tsconfig-paths/register src/modules/response-code/seeders/response-code.seed.ts
|
||||
```
|
||||
|
||||
**Expected Output**:
|
||||
```
|
||||
Seeding Response Codes...
|
||||
✓ 1A-1G Engineering codes created
|
||||
✓ 1A-1G Material codes created
|
||||
✓ 1A-1G Contract codes created
|
||||
✓ 1A-1G Testing codes created
|
||||
✓ 1A-1G ESG codes created
|
||||
✓ Codes 2, 3, 4 created (all categories)
|
||||
✓ Default rules applied for all document types
|
||||
Done! 55 response codes created.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Backend Setup
|
||||
|
||||
### 2.1 Install Dependencies
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2.2 Environment Variables
|
||||
|
||||
Add to `.env`:
|
||||
```env
|
||||
# Redis (for BullMQ and Redlock)
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# BullMQ Queues
|
||||
BULLMQ_QUEUE_PREFIX=rfa
|
||||
BULLMQ_REMINDER_QUEUE=rfa-reminders
|
||||
BULLMQ_DISTRIBUTION_QUEUE=rfa-distribution
|
||||
|
||||
# Reminder Schedule
|
||||
REMINDER_DAYS_BEFORE_DUE=2
|
||||
ESCALATION_DAYS_AFTER_DUE_L1=1
|
||||
ESCALATION_DAYS_AFTER_DUE_L2=3
|
||||
```
|
||||
|
||||
### 2.3 Start Development Server
|
||||
|
||||
```bash
|
||||
npm run start:dev
|
||||
```
|
||||
|
||||
Verify modules loaded:
|
||||
```
|
||||
[Nest] 1234 - 01/01/2024, 09:00:00 AM LOG [ReviewTeamModule] Module initialized
|
||||
[Nest] 1234 - 01/01/2024, 09:00:00 AM LOG [ResponseCodeModule] Module initialized
|
||||
[Nest] 1234 - 01/01/2024, 09:00:00 AM LOG [DelegationModule] Module initialized
|
||||
[Nest] 1234 - 01/01/2024, 09:00:00 AM LOG [ReminderModule] Module initialized
|
||||
[Nest] 1234 - 01/01/2024, 09:00:00 AM LOG [DistributionModule] Module initialized
|
||||
```
|
||||
|
||||
### 2.4 Start Queue Workers
|
||||
|
||||
```bash
|
||||
# Terminal 2 - Reminder processor
|
||||
npx ts-node -r tsconfig-paths/register src/modules/reminder/processors/reminder.processor.ts
|
||||
|
||||
# Terminal 3 - Distribution processor
|
||||
npx ts-node -r tsconfig-paths/register src/modules/distribution/processors/distribution.processor.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend Setup
|
||||
|
||||
### 3.1 Install Dependencies
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
### 3.2 Start Development Server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 3.3 Access URLs
|
||||
|
||||
- **RFA Review Page**: http://localhost:3000/dashboard/rfa/{id}/review
|
||||
- **Review Teams Admin**: http://localhost:3000/dashboard/review-teams
|
||||
- **Response Codes Admin**: http://localhost:3000/dashboard/response-codes
|
||||
- **Delegation Settings**: http://localhost:3000/dashboard/delegation
|
||||
|
||||
---
|
||||
|
||||
## 4. First Time Setup
|
||||
|
||||
### 4.1 Create Review Team
|
||||
|
||||
```bash
|
||||
# Using API
|
||||
curl -X POST http://localhost:3001/api/v1/review-teams \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"name": "Structural Review Team",
|
||||
"description": "Team for structural drawings review",
|
||||
"projectId": "019505a1-7c3e-7000-8000-abc123def456",
|
||||
"defaultForRfaTypes": ["SDW", "DDW"]
|
||||
}'
|
||||
```
|
||||
|
||||
### 4.2 Add Team Members
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/review-teams/{teamId}/members \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"userId": "019505a1-7c3e-7000-8000-abc123def789",
|
||||
"disciplineId": 1,
|
||||
"role": "LEAD"
|
||||
}'
|
||||
```
|
||||
|
||||
### 4.3 Setup Reminder Rules
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/reminder-rules \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"name": "RFA Due Soon Reminder",
|
||||
"triggerDaysBeforeDue": 2,
|
||||
"reminderType": "DUE_SOON",
|
||||
"recipients": ["ASSIGNEE", "MANAGER"],
|
||||
"messageTemplateTh": "RFA #{docNumber} ใกล้ครบกำหนดในอีก {days} วัน",
|
||||
"messageTemplateEn": "RFA #{docNumber} is due in {days} days"
|
||||
}'
|
||||
```
|
||||
|
||||
### 4.4 Setup Distribution Matrix
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/distribution-matrices \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"name": "Shop Drawing Distribution",
|
||||
"documentTypeId": 1,
|
||||
"conditions": {
|
||||
"codes": ["1A", "1B", "2"]
|
||||
},
|
||||
"recipients": [
|
||||
{ "recipientType": "ROLE", "recipientId": "SITE_TEAM", "deliveryMethod": "BOTH" },
|
||||
{ "recipientType": "ROLE", "recipientId": "QS_TEAM", "deliveryMethod": "EMAIL" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Test Workflow
|
||||
|
||||
### 5.1 Submit RFA with Review Team
|
||||
|
||||
```bash
|
||||
# Submit RFA - triggers workflow and creates review tasks
|
||||
curl -X POST http://localhost:3001/api/v1/rfa/{uuid}/submit \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"templateId": 1,
|
||||
"reviewTeamId": "019505a1-7c3e-7000-8000-abc123def456"
|
||||
}'
|
||||
```
|
||||
|
||||
### 5.2 Check Review Tasks Created
|
||||
|
||||
```bash
|
||||
# As reviewer, check inbox
|
||||
curl http://localhost:3001/api/v1/review-tasks \
|
||||
-H "Authorization: Bearer $REVIEWER_TOKEN"
|
||||
```
|
||||
|
||||
### 5.3 Complete Review with Response Code
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/review-tasks/{taskId} \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $REVIEWER_TOKEN" \
|
||||
-d '{
|
||||
"responseCodeId": "019505a1-7c3e-7000-8000-abc123def111",
|
||||
"comments": "Approved with minor comments on detail A3"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Verification Checklist
|
||||
|
||||
- [ ] Review Team created and visible in admin
|
||||
- [ ] Review Team members assigned by discipline
|
||||
- [ ] RFA submission creates parallel review tasks
|
||||
- [ ] Response codes filtered by document category
|
||||
- [ ] Code 3 triggers veto and returns workflow
|
||||
- [ ] Reminders scheduled on due date
|
||||
- [ ] Distribution executes after approval
|
||||
- [ ] Delegation forwards tasks correctly
|
||||
- [ ] Aggregate status shows real-time progress
|
||||
|
||||
---
|
||||
|
||||
## 7. Troubleshooting
|
||||
|
||||
### Queue Jobs Not Processing
|
||||
|
||||
```bash
|
||||
# Check BullMQ board (if enabled)
|
||||
open http://localhost:3001/admin/queues
|
||||
|
||||
# Or check Redis
|
||||
redis-cli KEYS "bull*"
|
||||
redis-cli LRANGE "bull:rfa-reminders:waiting" 0 -1
|
||||
```
|
||||
|
||||
### Parallel Review Not Working
|
||||
|
||||
Check workflow-engine DSL configuration:
|
||||
```bash
|
||||
# Verify parallel gateway enabled
|
||||
curl http://localhost:3001/api/v1/workflow-definitions/RFA_APPROVAL \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Response Codes Not Loading
|
||||
|
||||
```bash
|
||||
# Verify seed data
|
||||
mysql -u root -p lcbp3 -e "SELECT COUNT(*) FROM response_codes;"
|
||||
# Expected: 55 rows minimum
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Next Steps
|
||||
|
||||
1. **Run Tests**: `npm test` (backend), `npm run test:e2e` (frontend)
|
||||
2. **Load Test**: `k6 run load-tests/rfa-approval-load.js`
|
||||
3. **Deploy**: Follow `specs/04-Infrastructure-OPS/04-08-release-management-policy.md`
|
||||
@@ -0,0 +1,230 @@
|
||||
# Phase 0 Research: RFA Approval System Refactor
|
||||
|
||||
**Date**: 2026-05-11
|
||||
**Purpose**: Research technical patterns and validate design decisions
|
||||
|
||||
---
|
||||
|
||||
## Research Topics
|
||||
|
||||
### 1. Parallel Review in Workflow Engine
|
||||
|
||||
**Research Task**: Can Unified Workflow Engine (ADR-001) support parallel tasks with consensus rules?
|
||||
|
||||
**Decision**: ✅ **Yes, with DSL Extension**
|
||||
|
||||
**Rationale**:
|
||||
- Current DSL supports sequential states and transitions
|
||||
- Parallel review requires: (a) Task splitting on state entry, (b) Task aggregation before transition, (c) Consensus rule evaluation
|
||||
- Pattern: `ParallelGateState` - enters sub-workflows for each Discipline, aggregates on completion
|
||||
|
||||
**Implementation Pattern**:
|
||||
```typescript
|
||||
// DSL Extension: Parallel Gateway
|
||||
{
|
||||
type: 'parallel_gateway',
|
||||
config: {
|
||||
discriminator: 'discipline', // Split by discipline
|
||||
minCompletion: 'majority', // Consensus rule
|
||||
vetoConditions: [{ field: 'responseCode', value: '3' }] // Veto triggers
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Option A: Multiple Workflow Instances per RFA (rejected - too complex, hard to aggregate)
|
||||
- Option B: Sequential with fast-forward (rejected - doesn't truly parallelize)
|
||||
- Option C: **Parallel Gateway in DSL** (selected - clean abstraction, reusable pattern)
|
||||
|
||||
**References**:
|
||||
- BPMN 2.0 Parallel Gateway pattern
|
||||
- Existing `workflow-dsl.schema.ts` in codebase
|
||||
|
||||
---
|
||||
|
||||
### 2. Response Code Matrix Storage
|
||||
|
||||
**Research Task**: Best structure for Master Approval Matrix with 5 categories × 11 codes?
|
||||
|
||||
**Decision**: **Normalized Relational Model with JSON for flexibility**
|
||||
|
||||
**Rationale**:
|
||||
- Core codes (1A-1G, 2, 3, 4) are stable relational data
|
||||
- Category mappings (which codes apply to which doc types) need flexibility
|
||||
- Project overrides need inheritance tracking
|
||||
|
||||
**Schema Design**:
|
||||
```sql
|
||||
-- Core Response Codes (stable)
|
||||
response_codes (id, code, sub_status, description_th, description_en, category)
|
||||
|
||||
-- Matrix Rules (project-specific overrides)
|
||||
response_code_rules (
|
||||
id,
|
||||
project_id NULLABLE, -- NULL = global default
|
||||
document_type_id,
|
||||
response_code_id,
|
||||
is_enabled,
|
||||
requires_comments,
|
||||
triggers_notification,
|
||||
parent_rule_id -- For inheritance tracking
|
||||
)
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Single JSON column for entire matrix (rejected - hard to query, validate, index)
|
||||
- Full EAV (Entity-Attribute-Value) (rejected - too complex for this use case)
|
||||
|
||||
---
|
||||
|
||||
### 3. Delegation Pattern & Circular Detection
|
||||
|
||||
**Research Task**: Best approach for delegation with chain depth limit and circular detection?
|
||||
|
||||
**Decision**: **Adjacency List with Path Enumeration, Max Depth = 3**
|
||||
|
||||
**Rationale**:
|
||||
- Adjacency List: Simple, fast for immediate lookup (`delegator_id → delegatee_id`)
|
||||
- Path Enumeration: Store full chain as array for circular detection
|
||||
- Max Depth 3: Prevents runaway chains while supporting realistic use cases
|
||||
|
||||
**Circular Detection Algorithm**:
|
||||
```typescript
|
||||
function detectCircularDelegation(delegatorId: string, proposedDelegateeId: string): boolean {
|
||||
// BFS/DFS from proposedDelegatee, check if can reach delegatorId
|
||||
const visited = new Set<string>();
|
||||
const queue = [proposedDelegateeId];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift()!;
|
||||
if (current === delegatorId) return true; // Circular!
|
||||
if (visited.has(current)) continue;
|
||||
visited.add(current);
|
||||
|
||||
// Add all delegatees of current
|
||||
const delegatees = getActiveDelegations(current);
|
||||
queue.push(...delegatees.map(d => d.delegateeId));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Nested Set Model (rejected - overkill for simple chains)
|
||||
- Closure Table (rejected - requires maintenance on delegation expiry)
|
||||
|
||||
---
|
||||
|
||||
### 4. BullMQ Pattern for Reminders & Distribution
|
||||
|
||||
**Research Task**: Best BullMQ patterns for scheduled reminders and async distribution?
|
||||
|
||||
**Decision**: **Delayed Jobs + Repeatable Jobs + Flows**
|
||||
|
||||
**Pattern Breakdown**:
|
||||
|
||||
**Reminders**:
|
||||
- **Delayed Jobs**: Schedule individual reminder at due date
|
||||
- **Repeatable Jobs**: Daily reminder for overdue items (cron pattern)
|
||||
- **Job Data**: `{ rfaId, reviewerId, reminderType, escalationLevel }`
|
||||
|
||||
**Distribution**:
|
||||
- **Job Flow**: Parent (distribution coordinator) → Children (individual deliveries)
|
||||
- **Retry**: 3 attempts with exponential backoff
|
||||
- **Dead Letter**: Failed distributions logged for manual intervention
|
||||
|
||||
```typescript
|
||||
// Reminder Queue Pattern
|
||||
await reminderQueue.add('rfa-reminder', {
|
||||
rfaRevisionId,
|
||||
reviewerId,
|
||||
reminderType: 'DUE_SOON'
|
||||
}, {
|
||||
delay: calculateDelay(dueDate, reminderDaysBefore)
|
||||
});
|
||||
|
||||
// Distribution Flow Pattern
|
||||
await distributionFlow.add({
|
||||
name: 'rfa-distribution',
|
||||
data: { rfaId, responseCode, recipients: [...] },
|
||||
children: recipients.map(r => ({
|
||||
name: 'deliver-document',
|
||||
data: { recipientId: r.id, method: r.deliveryMethod }
|
||||
}))
|
||||
});
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- node-cron for scheduling (rejected - no persistence, no retry)
|
||||
- Custom scheduler service (rejected - BullMQ already provides this)
|
||||
|
||||
---
|
||||
|
||||
### 5. Review Task Status Aggregation
|
||||
|
||||
**Research Task**: How to efficiently calculate aggregate status for parallel reviews?
|
||||
|
||||
**Decision**: **Materialized View + Real-time Counter**
|
||||
|
||||
**Rationale**:
|
||||
- Materialized View: Fast reads for list views ("2 of 3 approved")
|
||||
- Real-time Counter: Immediate update on each review action
|
||||
- Trigger: Update counter on ReviewTask status change
|
||||
|
||||
**Aggregation Logic**:
|
||||
```sql
|
||||
-- Materialized view for fast reads
|
||||
CREATE VIEW review_task_summary AS
|
||||
SELECT
|
||||
rfa_revision_id,
|
||||
COUNT(*) as total_disciplines,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as completed,
|
||||
SUM(CASE WHEN response_code = '3' THEN 1 ELSE 0 END) as rejected_count
|
||||
FROM review_tasks
|
||||
GROUP BY rfa_revision_id;
|
||||
|
||||
-- Real-time consensus check
|
||||
SELECT CASE
|
||||
WHEN rejected_count > 0 THEN 'REJECTED' -- Veto triggered
|
||||
WHEN completed = total_disciplines THEN
|
||||
CASE
|
||||
WHEN approved_count / total_disciplines >= 0.5 THEN 'APPROVED'
|
||||
ELSE 'NEEDS_REVIEW'
|
||||
END
|
||||
ELSE 'IN_PROGRESS'
|
||||
END as consensus_status
|
||||
FROM review_task_summary;
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Calculate on-demand (rejected - slow with many disciplines)
|
||||
- Application-level cache (rejected - stale data risk)
|
||||
|
||||
---
|
||||
|
||||
## Summary of Decisions
|
||||
|
||||
| Topic | Decision | Key Rationale |
|
||||
|-------|----------|---------------|
|
||||
| Parallel Review | DSL Parallel Gateway | Clean abstraction, reusable |
|
||||
| Response Code Storage | Normalized + JSON | Balance of structure and flexibility |
|
||||
| Delegation | Adjacency List + Path Enum | Simple, sufficient for depth ≤3 |
|
||||
| Queue Pattern | BullMQ Delayed + Flows | Industry standard, reliable |
|
||||
| Status Aggregation | Materialized View + Counter | Fast reads, real-time updates |
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Probability | Mitigation |
|
||||
|------|-------------|------------|
|
||||
| DSL Parallel Gateway complexity | Medium | Prototype with simple 2-discipline case first |
|
||||
| Response Code migration from existing | Low | New tables, existing data untouched |
|
||||
| Performance on large Review Teams | Low | Pagination on aggregation, Redis caching |
|
||||
| Circular delegation algorithm | Low | Unit test with 3-level chains |
|
||||
|
||||
---
|
||||
|
||||
## Next Phase
|
||||
|
||||
**Phase 1**: Design data model and API contracts based on these research decisions.
|
||||
@@ -0,0 +1,274 @@
|
||||
# Feature Specification: RFA Approval System Refactor (TeamBinder/InEight-Style)
|
||||
|
||||
**Feature Branch**: `1-rfa-approval-refactor`
|
||||
**Created**: 2026-05-11
|
||||
**Status**: Draft
|
||||
**Input**: User description: "refactor ระบบการอนุมัติเอกสาร, RFA ให้เหมือนกับ TeamBinder หรือ InEight"
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
ปรับปรุงระบบการอนุมัติเอกสาร RFA (Request for Approval) ให้มีความยืดหยุ่นและครบถ้วนตามมาตรฐานระดับสูงของอุตสาหกรรมก่อสร้าง (TeamBinder, InEight) รองรับการทำงานแบบ Multi-Disciplinary Review, Response Codes มาตรฐาน, และ Distribution Matrix อัตโนมัติ
|
||||
|
||||
---
|
||||
|
||||
## User Scenarios & Testing
|
||||
|
||||
### User Story 1 - Review Teams by Discipline (Priority: P1)
|
||||
|
||||
ผู้จัดการโครงการสามารถกำหนดกลุ่มผู้ตรวจสอบ (Review Teams) ตามสาขาวิชา (Disciplines) แทนการระบุรายบุคคล เพื่อให้การมอบหมายงานยืดหยุ่นและรวดเร็ว
|
||||
|
||||
**Why this priority**: ฟีเจอร์หลักที่แยกระบบปัจจุบันกับมาตรฐานอุตสาหกรรม - ช่วยลดเวลาในการกำหนดผู้ตรวจสอบและรองรับการเปลี่ยนแปลงบุคคลในโครงการ
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยสร้าง Review Team ที่มีหลาย Disciplines และส่ง RFA เข้า workflow - ระบบต้องสามารถมอบหมายให้ทุก Discipline พร้อมกันได้
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** ผู้ใช้มีสิทธิ์จัดการ Review Teams, **When** สร้าง Review Team ใหม่ชื่อ "Structural Review Team" พร้อมกำหนด Disciplines: Structural, Civil, **Then** ระบบบันทึกทีมและสามารถใช้กับ RFA ได้
|
||||
2. **Given** RFA ถูกส่งเข้า workflow และใช้ Review Team ที่มี 3 Disciplines, **When** ระบบประมวลผล, **Then** สร้าง Review Tasks สำหรับแต่ละ Discipline พร้อมกัน
|
||||
3. **Given** Reviewer ในทีมเปลี่ยนตำแหน่งหรือลาออก, **When** Admin อัปเดตสมาชิกใน Review Team, **Then** RFA ที่รออยู่ต้องอัปเดตผู้รับผิดชอบโดยอัตโนมัติ
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Response Codes & Sub-Status (Priority: P1)
|
||||
|
||||
วิศวกรสามารถเลือก Response Code มาตรฐาน (1A, 1B, 1C, 2, 3, 4) พร้อม Sub-Status ที่บ่งบอกความหมายเฉพาะทาง เพื่อสื่อสารสถานะงานได้ชัดเจนและสอดคล้องกับมาตรฐานอุตสาหกรรม
|
||||
|
||||
**Why this priority**: ปรับปรุงการสื่อสารระหว่างฝ่ายต่างๆ ลดความกำกวมในการอนุมัติ และสนับสนุนการทำ Final Acceptance Certificate (FAC)
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยเปิดหน้าอนุมัติ RFA และตรวจสอบว่า Response Codes แสดงตาม Category ของเอกสาร - เลือก Code 1C ต้องมีการแจ้งเตือนฝ่ายสัญญา
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** RFA ประเภท Shop Drawing, **When** Reviewer เปิดหน้าอนุมัติ, **Then** แสดงเฉพาะ Response Codes ที่เกี่ยวข้องกับ Engineering/Drawings (1A-1G, 2, 3, 4)
|
||||
2. **Given** Reviewer เลือก Code 1C (Change Order) หรือ 1D (Alternative), **When** บันทึกการอนุมัติ, **Then** ระบบส่งแจ้งเตือนไปยังฝ่ายสัญญา/BOQ อัตโนมัติ
|
||||
3. **Given** RFA ได้รับ Code 2 (Approved as Noted), **When** ดูรายงานสถานะ, **Then** แสดงสถานะ "Approved with Minor Comments" พร้องานที่ต้องแก้ไข
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Delegation & Proxy (Priority: P2)
|
||||
|
||||
ผู้ตรวจสอบสามารถมอบหมายอำนาจ (Delegate) ให้ผู้อื่นทำงานแทนเมื่อไม่อยู่หรือไม่สะดวก โดยกำหนดระยะเวลาและขอบเขตอำนาจได้
|
||||
|
||||
**Why this priority**: ป้องกันการคั่งค้างของ workflow เมื่อผู้ตรวจสอบไม่ว่าง รองรับการทำงานแบบ Hybrid/Remote
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยผู้ใช้ A ตั้งค่า Delegate ให้ผู้ใช้ B แล้วส่ง RFA มา - ผู้ใช้ B ต้องเห็นงานใน Inbox และสามารถอนุมัติแทนได้
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** ผู้ใช้ต้องการลาพักร้อน 1 สัปดาห์, **When** ตั้งค่า Delegation ให้ colleague พร้อมระบุวันที่เริ่ม-สิ้นสุด, **Then** งานที่ส่งมาระหว่างนี้ไปที่ Delegatee โดยอัตโนมัติ
|
||||
2. **Given** มีงาน RFA รออยู่ใน Inbox และผู้ใช้ตั้งค่า Delegation ไว้, **When** มีการส่งงานใหม่เข้ามา, **Then** ระบบมอบหมายให้ Delegatee พร้อมแสดงว่าเป็น "Delegated from [Original User]"
|
||||
3. **Given** Delegation หมดอายุ, **When** มีงานใหม่ส่งมา, **Then** งานกลับมาที่ผู้ใช้เจ้าของงานเดิม และยกเลิกสิทธิ์ Delegatee
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Auto-Reminders & Escalation (Priority: P2)
|
||||
|
||||
ระบบส่งการแจ้งเตือนอัตโนมัติเมื่องานใกล้ครบกำหนด และส่งขึ้นระดับ (Escalate) เมื่อเกินกำหนด ตาม SLA ที่กำหนดไว้
|
||||
|
||||
**Why this priority**: ลดความล่าช้าใน workflow และปรับปรุง on-time delivery rate ของเอกสาร
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยสร้าง RFA ที่มี Due Date ในอีก 1 วัน และตรวจสอบว่าระบบส่ง Reminder ตามที่ตั้งค่าไว้
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** RFA มี Due Date ในอีก 2 วัน, **When** ถึงเวลา 9:00 ของวันที่ตั้งค่า Reminder, **Then** ส่งอีเมล/แจ้งเตือนไปยังผู้รับผิดชอบ
|
||||
2. **Given** RFA เกินกำหนด 1 วัน, **When** ถึงเงื่อนไข Escalation, **Then** ส่งแจ้งเตือนไปยัง Manager ของผู้รับผิดชอบ พร้อมสถานะ Overdue
|
||||
3. **Given** Admin ตั้งค่า Reminder Frequency เป็น "Daily" สำหรับงาน Overdue, **When** งานค้างเกินกำหนด, **Then** ส่ง Reminder ทุกวันจนกว่าจะดำเนินการ
|
||||
|
||||
---
|
||||
|
||||
### User Story 5 - Distribution Matrix (Priority: P2)
|
||||
|
||||
ระบบกระจายเอกสารอัตโนมัติไปยังผู้ที่เกี่ยวข้องหลังจาก RFA ได้รับการอนุมัติ ตาม Distribution Matrix ที่กำหนดไว้ตามประเภทเอกสารและ Response Code
|
||||
|
||||
**Why this priority**: ลด manual work ในการส่งเอกสารต่อ ป้องกันการส่งตกหล่น และสนับสนุนการทำ Transmittal อัตโนมัติ
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยอนุมัติ RFA ที่มี Distribution Matrix แล้วตรวจสอบว่าเอกสารถูกส่งไปยังผู้รับที่กำหนดใน Matrix โดยอัตโนมัติ
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** RFA ประเภท Shop Drawing ได้รับ Code 1A (Full Approval), **When** อนุมัติสำเร็จ, **Then** ระบบส่งเอกสารไปยัง Site Team, QS, และ Document Control ตาม Distribution Matrix
|
||||
2. **Given** Distribution Matrix มีเงื่อนไข "Send only if Code = 1A, 1B, or 2", **When** RFA ได้รับ Code 3 (Rejected), **Then** ไม่ส่งเอกสารตาม Matrix (แจ้งเฉพาะผู้ส่ง)
|
||||
3. **Given** Admin อัปเดต Distribution Matrix เพิ่มผู้รับใหม่, **When** RFA ถัดไปได้รับการอนุมัติ, **Then** ส่งเอกสารไปยังผู้รับใหม่ด้วย
|
||||
|
||||
---
|
||||
|
||||
### User Story 6 - Master Approval Matrix Management (Priority: P3)
|
||||
|
||||
ผู้ดูแลระบบสามารถจัดการ Master Approval Matrix ที่เป็นมาตรฐานขององค์กร แยกตามหมวดงานและสถานะย่อย ให้ใช้งานทั่วทั้งโครงการ
|
||||
|
||||
**Why this priority**: มาตรฐานการอนุมัติให้สอดคล้องกับบริษัทและอุตสาหกรรม ลดความสับสนในการใช้ Response Codes
|
||||
|
||||
**Independent Test**: สามารถทดสอบโดยสร้าง Master Approval Matrix ใหม่ และตรวจสอบว่า RFA แสดง Response Codes ตาม Matrix ที่กำหนด
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** Admin ต้องการสร้างมาตรฐานใหม่สำหรับโครงการก่อสร้างสะพาน, **When** สร้าง Master Approval Matrix พร้อมกำหนดหมวดงานและ Sub-status, **Then** สามารถใช้กับโครงการที่เลือกได้
|
||||
2. **Given** Master Approval Matrix ถูกใช้งานในโครงการหลายแห่ง, **When** Admin แก้ไข Matrix, **Then** ระบบแสดง Warning ว่าจะมีผลกับโครงการที่ใช้งานอยู่
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
1. **Race Condition**: สอง Reviewer ในทีมเดียวกันกดอนุมัติพร้อมกัน - ระบบต้องจัดการด้วย Optimistic Locking หรือ Redlock
|
||||
2. **Circular Delegation**: ผู้ใช้ A Delegate ให้ B, B Delegate ให้ C, C พยายาม Delegate ให้ A - ระบบต้องตรวจจับและป้องกัน
|
||||
3. **Expired Review Task**: Review Task ค้างนานเกินกำหนดและถูก Reassign - ต้องบันทึกประวัติการเปลี่ยนแปลง
|
||||
4. **Invalid Response Code**: Reviewer พยายามใช้ Response Code ที่ไม่สอดคล้องกับ Category ของเอกสาร - ระบบต้องแสดงข้อผิดพลาดและไม่บันทึก
|
||||
5. **Concurrent Review**: หลาย Disciplines ต้อง Review พร้อมกัน แต่มี Discipline หนึ่งปฏิเสธ - ต้องหยุด workflow และแจ้งผู้ส่ง
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
**Review Teams & Disciplines**
|
||||
- **FR-001**: ระบบ MUST รองรับการสร้าง Review Teams ที่มีหลาย Disciplines
|
||||
- **FR-002**: Review Teams MUST สามารถกำหนดเป็น Default ตามประเภท RFA ได้
|
||||
- **FR-003**: เมื่อ RFA เข้า workflow ที่ใช้ Review Team, ระบบ MUST สร้าง Review Tasks สำหรับแต่ละ Discipline พร้อมกัน (Parallel Review)
|
||||
- **FR-004**: Review Tasks MUST แสดงสถานะรวม (Aggregate Status) เช่น "2 of 3 Disciplines Approved"
|
||||
- **FR-004.5**: Parallel Review MUST ใช้กฎ Majority with Veto - หากส่วนใหญ่อนุมัติให้ผ่าน แต่หากมี Discipline ใดให้ Code 3 (Rejected) ต้องหยุด workflow และส่งกลับให้แก้ไข
|
||||
|
||||
**Response Codes & Master Approval Matrix**
|
||||
- **FR-005**: ระบบ MUST ใช้ Master Approval Matrix ตามมาตรฐานที่กำหนด
|
||||
- **FR-006**: Response Codes MUST แสดงตาม Category ของเอกสาร (Engineering, Material, Contract, Testing, ESG)
|
||||
- **FR-007**: Code 1C (Change Order), 1D (Alternative), 3 (Rejected) MUST trigger การแจ้งเตือนไปยังฝ่ายที่เกี่ยวข้องโดยอัตโนมัติ
|
||||
- **FR-008**: ระบบ MUST บันทึกประวัติการเปลี่ยน Response Code (Audit Trail)
|
||||
- **FR-009**: Reviewer MUST สามารถเพิ่ม Comments พร้อม Response Code ได้
|
||||
|
||||
**Delegation & Proxy**
|
||||
- **FR-010**: ผู้ใช้ MUST สามารถตั้งค่า Delegation ได้ พร้อมกำหนดระยะเวลาเริ่มต้น-สิ้นสุด
|
||||
- **FR-011**: Delegation MUST รองรับการกำหนด Scope (เฉพาะบางประเภทเอกสาร หรือทั้งหมด)
|
||||
- **FR-012**: ระบบ MUST ตรวจจับ Circular Delegation และป้องกัน
|
||||
- **FR-013**: เมื่อ Delegation หมดอายุ, งานใหม่ MUST กลับไปยังผู้ใช้เดิมโดยอัตโนมัติ
|
||||
- **FR-014**: Delegatee MUST เห็นงานที่ได้รับมอบหมายแยกจากงานของตนเอง (Badge "Delegated")
|
||||
|
||||
**Auto-Reminders & Escalation**
|
||||
- **FR-015**: ระบบ MUST ส่ง Reminder ตาม Schedule ที่กำหนด (1 วันก่อน Due, วัน Due, ทุกวันหลัง Due)
|
||||
- **FR-016**: Escalation MUST ส่งแจ้งเตือนไปยัง Manager เมื่องาน Overdue ตามเกณฑ์ที่ตั้งไว้
|
||||
- **FR-017**: Admin MUST สามารถตั้งค่า Reminder Rules ต่อโครงการ/ประเภทเอกสารได้
|
||||
- **FR-018**: ระบบ MUST บันทึกประวัติการส่ง Reminder (ส่งเมื่อไหร่, ใครได้รับ)
|
||||
|
||||
**Frontend Workflow Visualization**
|
||||
- **FR-019.1**: Parallel Review MUST แสดงผลด้วย **Horizontal Stepper** - แถบความคืบหนันแนวนอนแสดงสถานะแต่ละ Discipline [Structural ▓▓▓░] [Civil ▓▓▓▓] [MEP ▓░░░]
|
||||
- **FR-019.2**: เมื่อมี Code 3 (Rejected) จาก Discipline ใด MUST แสดง **Modal Dialog** แจ้งการ Veto พร้อมรายละเอียด Discipline ที่ reject
|
||||
- **FR-019.3**: Navigation ระหว่าง Discipline details MUST ใช้ **Side Panel** layout - ซ้ายรายการ Disciplines, ขวาแสดงรายละเอียด Comments + Attachments ของ Discipline ที่เลือก
|
||||
- **FR-019.4**: Project Manager MUST สามารถ **Override Veto** ได้ - บังคับผ่าน RFA แม้มี Code 3 จาก Discipline พร้อมบันทึกเหตุผล, Audit trail ว่าเป็น forced approval, และแจ้งเตือนทุก stakeholder ที่เกี่ยวข้อง
|
||||
|
||||
**Distribution Matrix**
|
||||
- **FR-019**: Distribution Matrix MUST กำหนดผู้รับตาม: ประเภทเอกสาร + Response Code + สถานะเอกสาร
|
||||
- **FR-020**: ระบบ MUST สร้าง Transmittal Records อัตโนมัติหลังการอนุมัติตาม Distribution Matrix ผ่าน BullMQ Queue (Async, ภายใน 5 นาที)
|
||||
- **FR-021**: Distribution Matrix MUST รองรับเงื่อนไข "Send Only If" (เช่น Code 1A, 1B เท่านั้น)
|
||||
- **FR-022**: ระบบ MUST แสดงรายงาน Distribution Status (ส่งแล้วกี่ราย, ค้างกี่ราย)
|
||||
|
||||
**Integration with Existing Systems**
|
||||
- **FR-023**: ต้องใช้ Unified Workflow Engine (ADR-001) ที่มีอยู่แล้ว
|
||||
- **FR-024**: ต้องใช้ BullMQ (ADR-008) สำหรับ Reminders และ Distribution Jobs
|
||||
- **FR-025**: ต้องใช้ CASL (ADR-016) สำหรับสิทธิ์ Reviewer และ Delegation
|
||||
|
||||
### Key Entities
|
||||
|
||||
**ReviewTeam**
|
||||
- ตัวแทนกลุ่มผู้ตรวจสอบที่จัดการตามสาขาวิชา
|
||||
- Attributes: name, description, projectId, defaultForRfaTypes, isActive
|
||||
- Relationships: has many ReviewTeamMember, has many ReviewTask
|
||||
|
||||
**ReviewTeamMember**
|
||||
- สมาชิกใน Review Team พร้อม Discipline ที่รับผิดชอบ
|
||||
- Attributes: teamId, userId, disciplineId, role, priorityOrder
|
||||
- Relationships: belongs to ReviewTeam, belongs to User, belongs to Discipline
|
||||
|
||||
**ReviewTask**
|
||||
- งานตรวจสอบที่สร้างเมื่อ RFA เข้า workflow
|
||||
- Attributes: rfaRevisionId, teamId, disciplineId, assignedToUserId, status, dueDate, responseCode, comments
|
||||
- Relationships: belongs to RfaRevision, belongs to ReviewTeam
|
||||
|
||||
**ResponseCodeMatrix**
|
||||
- Master Approval Matrix ที่กำหนด Response Codes ตาม Category
|
||||
- Attributes: code, subStatus, category, descriptionTh, descriptionEn, implications, requiresNotificationTo
|
||||
- Relationships: has many ResponseCodeRule
|
||||
|
||||
**ResponseCodeRule**
|
||||
- กฎการใช้ Response Code ตามประเภทเอกสาร
|
||||
- Attributes: matrixId, documentTypeId, isEnabled, requiresComments, triggersNotification
|
||||
|
||||
**Delegation**
|
||||
- การมอบหมายอำนาจจากผู้ใช้หนึ่งไปอีกผู้ใช้
|
||||
- Attributes: delegatorId, delegateeId, startDate, endDate, scope, documentTypes, isActive
|
||||
- Relationships: belongs to User (delegator), belongs to User (delegatee)
|
||||
|
||||
**ReminderRule**
|
||||
- กฎการส่ง Reminder ตาม SLA
|
||||
- Attributes: name, projectId, documentTypeId, triggerDays, reminderType, recipients, messageTemplate
|
||||
- Relationships: has many ReminderSchedule
|
||||
|
||||
**DistributionMatrix**
|
||||
- กำหนดการกระจายเอกสารหลังอนุมัติ
|
||||
- Attributes: name, documentTypeId, responseCode, status, recipients, conditions, isActive
|
||||
- Relationships: has many DistributionRecipient
|
||||
|
||||
**DistributionRecipient**
|
||||
- ผู้รับเอกสารใน Distribution Matrix
|
||||
- Attributes: matrixId, recipientType (USER/ORGANIZATION/TEAM), recipientId, deliveryMethod
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: ผู้ใช้สามารถกำหนด Review Team ได้ในเวลาน้อยกว่า 2 นาที (เทียบกับระบบเดิมที่ต้องเลือกรายบุคคล)
|
||||
- **SC-002**: Response Code ถูกใช้งานได้ถูกต้อง 95%+ ของเวลาทั้งหมด (ลดการใช้ผิดประเภท)
|
||||
- **SC-003**: ลดเวลาในการอนุมัติ RFA โดยเฉลี่ย 30% จากการใช้ Parallel Review และ Response Codes ที่ชัดเจน
|
||||
- **SC-004**: 0% ของเอกสารที่ค้างงานเนื่องจากผู้ตรวจสอบไม่อยู่ (ใช้ Delegation)
|
||||
- **SC-005**: 100% ของเอกสารที่อนุมัติถูกกระจายตาม Distribution Matrix โดยไม่ต้อง manual intervention
|
||||
- **SC-006**: On-time delivery rate ของ RFA ปรับปรุงจาก baseline เป็นอย่างน้อย 85%
|
||||
- **SC-007**: ผู้ใช้พึงพอใจกับระบบอนุมัติใหม่ (NPS > 40)
|
||||
|
||||
---
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-05-11
|
||||
|
||||
- **Q1**: Master Approval Matrix scope and inheritance model? → **A**: Global base + Project overrides - Default Matrix inherited organization-wide, projects can override specific codes/rules as needed (Option B)
|
||||
- **Q2**: Parallel Review consensus model when Disciplines have conflicting decisions? → **A**: Majority with veto - All Disciplines submit responses, majority determines outcome, but Code 3 (Rejected) from any Discipline vetoes approval and requires revision (Option C)
|
||||
- **Q3**: Escalation chain depth for overdue RFA reviews? → **A**: 2 levels - Direct manager first, then Project Manager/Director if still unresolved after additional delay (Option B)
|
||||
- **Q4**: Distribution Matrix execution timing relative to approval? → **A**: Async after approval - Approval returns immediately, distribution queued via BullMQ and processed automatically within 5 minutes (Option C)
|
||||
- **Q5**: Frontend pattern for displaying Parallel Review progress? → **A**: Horizontal Stepper - แถบความคืบหนันแนวนอนแสดงสถานะแต่ละ Discipline (Option A)
|
||||
- **Q6**: How to display Veto/Consensus status when Code 3 triggered? → **A**: Modal Dialog - Popup แจ้งเมื่อมีการ Veto พร้อมรายละเอียด Discipline ที่ reject (Option B)
|
||||
- **Q7**: Navigation pattern between Discipline details for Reviewer? → **A**: Side Panel - ซ้ายรายการ Disciplines, ขวาแสดงรายละเอียดที่เลือก (Option D)
|
||||
- **Q8**: Can Veto be overridden and by whom? → **A**: Project Manager Override - PM สามารถบังคับผ่าน Veto ได้ พร้อมบันทึกเหตุผล และแจ้งเตือนทุก stakeholder (Option B)
|
||||
|
||||
---
|
||||
|
||||
## Assumptions
|
||||
|
||||
1. **ผู้ใช้มีความคุ้นเคยกับ Response Codes มาตรฐานอุตสาหกรรม** - จำเป็นต้องมี Training Material ประกอบ
|
||||
2. **โครงสร้าง Discipline Master Data มีอยู่แล้ว** - ใช้จากระบบ User Management ที่มีอยู่
|
||||
3. **Unified Workflow Engine (ADR-001) มีความสามารถรองรับ Parallel Tasks** - อาจต้องปรับปรุง DSL
|
||||
4. **BullMQ Infrastructure พร้อมใช้งาน** - สำหรับ Reminders และ Background Jobs
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **ADR-001**: Unified Workflow Engine (ต้องรองรับ Parallel Review Tasks)
|
||||
- **ADR-008**: BullMQ Notification Strategy
|
||||
- **ADR-016**: CASL Authorization (สำหรับ Reviewer สิทธิ์)
|
||||
- **ADR-019**: UUID Strategy (สำหรับ Entities ใหม่)
|
||||
- **ADR-021**: Workflow Context (สำหรับ Step Attachments)
|
||||
- **Existing**: Discipline, User, Organization Master Data
|
||||
|
||||
---
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
|------|--------|------------|
|
||||
| ผู้ใช้ไม่คุ้นเคยกับ Response Codes ใหม่ | High | ทำ Training Workshop พร้อม Quick Reference Guide |
|
||||
| Workflow Engine ไม่รองรับ Parallel Review | High | ประเมิน DSL ก่อนเริ่ม, อาจต้อง Refactor Engine |
|
||||
| Performance ช้าจาก Complex Matrix Lookup | Medium | ทำ Caching สำหรับ Matrix และ Rules |
|
||||
| Circular Delegation ซับซ้อน | Low | Validation ตั้งแต่ต้น, Limit depth ของ chain |
|
||||
@@ -0,0 +1,301 @@
|
||||
# Implementation Tasks: RFA Approval System Refactor
|
||||
|
||||
**Feature**: RFA Approval System Refactor (TeamBinder/InEight-Style)
|
||||
**Branch**: `1-rfa-approval-refactor`
|
||||
**Generated**: 2026-05-11
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup & Infrastructure
|
||||
|
||||
### Goal
|
||||
Initialize project structure and shared infrastructure for all modules.
|
||||
|
||||
**Independent Test**: All new modules compile without errors, BullMQ queues connect to Redis.
|
||||
|
||||
---
|
||||
|
||||
- [ ] T001 [P] Create SQL schema file `specs/03-Data-and-Storage/lcbp3-v1.9.0-rfa-approval-schema.sql` with all 9 new entities
|
||||
- [ ] T002 [P] Create Response Code seeder `backend/src/modules/response-code/seeders/response-code.seed.ts`
|
||||
- [ ] T003 Create BullMQ queue configuration `backend/src/config/bullmq.config.ts`
|
||||
- [ ] T004 [P] Setup Redis connection for BullMQ and Redlock `backend/src/config/redis.config.ts`
|
||||
- [ ] T005 Create shared DTOs and enums `backend/src/modules/review-team/dto/shared/` (ReviewTaskStatus, ResponseCodeCategory, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational Entities & Services
|
||||
|
||||
### Goal
|
||||
Core entities required by multiple user stories. Must complete before US1-US6.
|
||||
|
||||
**Independent Test**: CRUD operations work for all entities via API.
|
||||
|
||||
---
|
||||
|
||||
- [ ] T006 [P] Create ReviewTeam entity `backend/src/modules/review-team/entities/review-team.entity.ts`
|
||||
- [ ] T007 [P] Create ReviewTeamMember entity `backend/src/modules/review-team/entities/review-team-member.entity.ts`
|
||||
- [ ] T008 Create ResponseCode entity `backend/src/modules/response-code/entities/response-code.entity.ts`
|
||||
- [ ] T009 [P] Create ResponseCodeRule entity `backend/src/modules/response-code/entities/response-code-rule.entity.ts`
|
||||
- [ ] T010 [P] Create ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [ ] T011 Create ResponseCodeModule with service `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [ ] T012 Create ResponseCodeController with basic CRUD `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [ ] T013 Create ReviewTeamModule base structure `backend/src/modules/review-team/review-team.module.ts`
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Review Teams by Discipline (P1)
|
||||
|
||||
### Goal
|
||||
Users can create Review Teams with multiple Disciplines, and teams auto-assign to RFA types.
|
||||
|
||||
**Independent Test**:
|
||||
- Create Review Team via API with 3 disciplines
|
||||
- Verify team appears in list with member count
|
||||
- Submit RFA with team → parallel review tasks created
|
||||
|
||||
---
|
||||
|
||||
- [ ] T014 [US1] Create ReviewTeamService with CRUD and member management `backend/src/modules/review-team/review-team.service.ts`
|
||||
- [ ] T015 [P] [US1] Create ReviewTeamController endpoints `backend/src/modules/review-team/review-team.controller.ts`
|
||||
- [ ] T016 [US1] Create ReviewTaskService with assignment logic `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [ ] T017 [P] [US1] Integrate Review Team selection in RFA submission flow `backend/src/modules/rfa/rfa.service.ts`
|
||||
- [ ] T018 [US1] Implement parallel task creation on RFA submit `backend/src/modules/review-team/services/task-creation.service.ts`
|
||||
- [ ] T019 [P] [US1] Create Review Team management UI page `frontend/src/app/(dashboard)/review-teams/page.tsx`
|
||||
- [ ] T020 [P] [US1] Create Review Team form component `frontend/src/components/review-team/ReviewTeamForm.tsx`
|
||||
- [ ] T021 [US1] Create Team Member assignment component `frontend/src/components/review-team/TeamMemberManager.tsx`
|
||||
- [ ] T022 [P] [US1] Create useReviewTeams hook `frontend/src/hooks/use-review-teams.ts`
|
||||
- [ ] T023 [US1] Add Review Team selector to RFA submission form `frontend/src/app/(dashboard)/rfa/[id]/submit/page.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Response Codes & Master Approval Matrix (P1)
|
||||
|
||||
### Goal
|
||||
Response Codes display by document category, Code 1C/1D/3 trigger notifications, full audit trail.
|
||||
|
||||
**Independent Test**:
|
||||
- RFA review page shows only Engineering codes for Shop Drawing
|
||||
- Select Code 1C → notification sent to Contract team
|
||||
- Change response code → audit logged
|
||||
|
||||
---
|
||||
|
||||
- [ ] T024 [US2] Extend ResponseCodeService with category filtering `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [ ] T025 [P] [US2] Create ResponseCode lookup endpoint by document type `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [ ] T026 [US2] Implement Response Code implications evaluator `backend/src/modules/response-code/services/implications.service.ts`
|
||||
- [ ] T027 [P] [US2] Create notification trigger service for critical codes `backend/src/modules/response-code/services/notification-trigger.service.ts`
|
||||
- [ ] T028 [US2] Add audit logging for Response Code changes `backend/src/modules/response-code/services/audit.service.ts`
|
||||
- [ ] T029 [P] [US2] Create Response Code selector component with category filtering `frontend/src/components/response-code/ResponseCodeSelector.tsx`
|
||||
- [ ] T030 [US2] Create Response Code implications display `frontend/src/components/response-code/CodeImplications.tsx`
|
||||
- [ ] T031 [P] [US2] Create Master Approval Matrix admin UI `frontend/src/app/(dashboard)/response-codes/page.tsx`
|
||||
- [ ] T032 [US2] Create useResponseCodes hook with category filter `frontend/src/hooks/use-response-codes.ts`
|
||||
- [ ] T033 [P] [US2] Integrate Response Code selector in Review Task completion UI `frontend/src/components/review-task/CompleteReviewForm.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Delegation & Proxy (P2)
|
||||
|
||||
### Goal
|
||||
Users can delegate review tasks with date range, circular detection prevents loops.
|
||||
|
||||
**Independent Test**:
|
||||
- User A delegates to User B for 1 week
|
||||
- RFA assigned to A during period → automatically assigned to B
|
||||
- Try to create A→B→C→A → error prevented
|
||||
|
||||
---
|
||||
|
||||
- [ ] T034 [US3] Create Delegation entity `backend/src/modules/delegation/entities/delegation.entity.ts`
|
||||
- [ ] T035 [P] [US3] Create DelegationService with CRUD `backend/src/modules/delegation/delegation.service.ts`
|
||||
- [ ] T036 [US3] Implement circular delegation detection algorithm `backend/src/modules/delegation/services/circular-detection.service.ts`
|
||||
- [ ] T037 [P] [US3] Create DelegationController endpoints `backend/src/modules/delegation/delegation.controller.ts`
|
||||
- [ ] T038 [US3] Integrate delegation resolution in ReviewTaskService `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [ ] T039 [P] [US3] Create Delegation settings UI page `frontend/src/app/(dashboard)/delegation/page.tsx`
|
||||
- [ ] T040 [US3] Create Delegation form with date picker `frontend/src/components/delegation/DelegationForm.tsx`
|
||||
- [ ] T041 [P] [US3] Create delegated task indicator ("Delegated from X") `frontend/src/components/review-task/DelegatedBadge.tsx`
|
||||
- [ ] T042 [P] [US3] Create useDelegation hook `frontend/src/hooks/use-delegation.ts`
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 4 - Auto-Reminders & Escalation (P2)
|
||||
|
||||
### Goal
|
||||
Scheduled reminders via BullMQ, 2-level escalation when overdue.
|
||||
|
||||
**Independent Test**:
|
||||
- RFA due in 2 days → reminder scheduled
|
||||
- Past due date → escalation level 1 notification
|
||||
- 3 days overdue → escalation level 2 notification
|
||||
|
||||
---
|
||||
|
||||
- [ ] T043 [US4] Create ReminderRule entity `backend/src/modules/reminder/entities/reminder-rule.entity.ts`
|
||||
- [ ] T044 [P] [US4] Create ReminderService with BullMQ integration `backend/src/modules/reminder/reminder.service.ts`
|
||||
- [ ] T045 [US4] Implement reminder scheduling on RFA submit `backend/src/modules/reminder/services/scheduler.service.ts`
|
||||
- [ ] T046 [P] [US4] Create ReminderProcessor for queue workers `backend/src/modules/reminder/processors/reminder.processor.ts`
|
||||
- [ ] T047 [US4] Implement 2-level escalation logic `backend/src/modules/reminder/services/escalation.service.ts`
|
||||
- [ ] T048 [P] [US4] Create ReminderRuleController admin endpoints `backend/src/modules/reminder/reminder.controller.ts`
|
||||
- [ ] T049 [P] [US4] Create ReminderRule admin UI `frontend/src/app/(dashboard)/reminder-rules/page.tsx`
|
||||
- [ ] T050 [US4] Create reminder history viewer `frontend/src/components/reminder/ReminderHistory.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: User Story 5 - Distribution Matrix (P2)
|
||||
|
||||
### Goal
|
||||
Async distribution after approval, Transmittal records created via BullMQ.
|
||||
|
||||
**Independent Test**:
|
||||
- RFA approved with Code 1A → distribution queued
|
||||
- Distribution job processed within 5 minutes
|
||||
- Recipients receive email and in-app notification
|
||||
|
||||
---
|
||||
|
||||
- [ ] T051 [US5] Create DistributionMatrix entity `backend/src/modules/distribution/entities/distribution-matrix.entity.ts`
|
||||
- [ ] T052 [P] [US5] Create DistributionRecipient entity `backend/src/modules/distribution/entities/distribution-recipient.entity.ts`
|
||||
- [ ] T053 [US5] Create DistributionMatrixService with CRUD `backend/src/modules/distribution/distribution-matrix.service.ts`
|
||||
- [ ] T054 [P] [US5] Create DistributionService with BullMQ integration `backend/src/modules/distribution/distribution.service.ts`
|
||||
- [ ] T055 [US5] Implement distribution triggering on approval `backend/src/modules/distribution/services/approval-listener.service.ts`
|
||||
- [ ] T056 [P] [US5] Create DistributionProcessor for queue workers `backend/src/modules/distribution/processors/distribution.processor.ts`
|
||||
- [ ] T057 [US5] Create Transmittal records from distribution `backend/src/modules/distribution/services/transmittal-creator.service.ts`
|
||||
- [ ] T058 [P] [US5] Create DistributionMatrixController `backend/src/modules/distribution/distribution.controller.ts`
|
||||
- [ ] T059 [P] [US5] Create Distribution Matrix admin UI `frontend/src/app/(dashboard)/distribution-matrices/page.tsx`
|
||||
- [ ] T060 [US5] Create distribution status dashboard `frontend/src/components/distribution/DistributionStatus.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: User Story 6 - Master Approval Matrix Management (P3)
|
||||
|
||||
### Goal
|
||||
Admin UI for managing Matrix, project overrides with inheritance tracking.
|
||||
|
||||
**Independent Test**:
|
||||
- View global Matrix with all categories and codes
|
||||
- Create project-specific override for Code 1C
|
||||
- Override appears only for that project
|
||||
|
||||
---
|
||||
|
||||
- [ ] T061 [US6] Extend ResponseCodeService with project overrides `backend/src/modules/response-code/services/matrix-management.service.ts`
|
||||
- [ ] T062 [P] [US6] Create Matrix inheritance resolver `backend/src/modules/response-code/services/inheritance.service.ts`
|
||||
- [ ] T063 [US6] Add Matrix management endpoints to ResponseCodeController `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [ ] T064 [P] [US6] Create Master Approval Matrix visual editor `frontend/src/components/response-code/MatrixEditor.tsx`
|
||||
- [ ] T065 [US6] Create project override management UI `frontend/src/components/response-code/ProjectOverrideManager.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: Cross-Cutting & Polish
|
||||
|
||||
### Goal
|
||||
Workflow Engine integration, aggregate status, edge case handling, testing.
|
||||
|
||||
**Independent Test**:
|
||||
- Complete end-to-end workflow: RFA submit → parallel review → consensus → distribution
|
||||
- All edge cases handled (race conditions, circular delegation, veto)
|
||||
|
||||
---
|
||||
|
||||
- [ ] T066 Extend WorkflowEngine DSL with Parallel Gateway support `backend/src/modules/workflow-engine/dsl/parallel-gateway.handler.ts`
|
||||
- [ ] T067 [P] Implement Review Task aggregate status calculator `backend/src/modules/review-team/services/aggregate-status.service.ts`
|
||||
- [ ] T068 [P] Create consensus evaluation service `backend/src/modules/review-team/services/consensus.service.ts`
|
||||
- [ ] T068.5 Implement Veto Override for Project Manager `backend/src/modules/review-team/services/veto-override.service.ts` - พร้อม audit trail และ notification
|
||||
- [ ] T069 Implement race condition handling (Redlock) in ReviewTask completion `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [ ] T070 [P] Add optimistic locking to ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [ ] T071 Create Review Task inbox UI with aggregate status `frontend/src/components/review-task/ReviewTaskInbox.tsx`
|
||||
- [ ] T072 [P] Create parallel review progress indicator `frontend/src/components/review-task/ParallelProgress.tsx`
|
||||
- [ ] T072.5 Create Veto Override button and modal for PM `frontend/src/components/review-task/VetoOverrideDialog.tsx` - พร้อม input สำหรับ justification reason
|
||||
- [ ] T073 Add validation for all edge cases in service layer `backend/src/common/validators/review-validators.ts`
|
||||
- [ ] T074 [P] Create unit tests for ResponseCodeService `backend/tests/unit/response-code/response-code.service.spec.ts`
|
||||
- [ ] T075 [P] Create unit tests for Delegation circular detection `backend/tests/unit/delegation/circular-detection.service.spec.ts`
|
||||
- [ ] T076 [P] Create integration tests for parallel review consensus `backend/tests/integration/review-team/parallel-review.spec.ts`
|
||||
- [ ] T077 Create e2e tests for complete RFA workflow `backend/tests/e2e/rfa-workflow.e2e-spec.ts`
|
||||
- [ ] T078 [P] Add frontend tests for ResponseCodeSelector `frontend/tests/components/ResponseCodeSelector.test.tsx`
|
||||
- [ ] T079 Update quickstart.md with final setup instructions `specs/1-rfa-approval-refactor/quickstart.md`
|
||||
- [ ] T080 [P] Run full test suite and fix any failures `npm test`
|
||||
|
||||
---
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
Phase 1: Setup
|
||||
│
|
||||
▼
|
||||
Phase 2: Foundational Entities
|
||||
│
|
||||
├───> Phase 3: US1 Review Teams ───────┐
|
||||
│ │
|
||||
├───> Phase 4: US2 Response Codes ────┼──┐
|
||||
│ │ │
|
||||
├───> Phase 5: US3 Delegation ────────┤ │
|
||||
│ │ │
|
||||
├───> Phase 6: US4 Reminders ──────────┤ │
|
||||
│ │ │
|
||||
└───> Phase 7: US5 Distribution ───────┼──┤
|
||||
│ │
|
||||
Phase 8: US6 Matrix Management <──────────┘ │
|
||||
│
|
||||
Phase 9: Polish & Integration <───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parallel Execution Opportunities
|
||||
|
||||
| Phase | Parallel Tasks | Description |
|
||||
|-------|----------------|-------------|
|
||||
| Phase 1 | T001, T002, T004, T005 | SQL, Seeder, Redis config, DTOs |
|
||||
| Phase 2 | T006, T007, T009, T010 | Entity creation |
|
||||
| Phase 3 | T015, T019, T020, T022 | Controller + Frontend components |
|
||||
| Phase 4 | T025, T027, T029, T031 | API + UI parallel |
|
||||
| Phase 5 | T035, T037, T039, T040, T042 | Backend + Frontend |
|
||||
| Phase 6 | T044, T046, T049 | Reminder service + processor + UI |
|
||||
| Phase 7 | T052, T054, T056, T058, T059 | Distribution entities + service + processor + UI |
|
||||
| Phase 9 | T067, T068, T070, T074, T075, T078 | Status calc + Locking + Tests |
|
||||
|
||||
---
|
||||
|
||||
## MVP Scope (Minimum Viable Product)
|
||||
|
||||
For fastest value delivery, implement:
|
||||
|
||||
1. **Phase 1-2**: Setup and entities
|
||||
2. **Phase 3**: US1 Review Teams only
|
||||
3. **Phase 9**: Basic consensus + edge case handling (skip US2-US6)
|
||||
|
||||
**MVP Deliverables**:
|
||||
- Review Teams with Disciplines
|
||||
- Parallel review task creation
|
||||
- Basic response code selection (no category filtering)
|
||||
- Simple sequential workflow (no parallel gateway in DSL yet)
|
||||
|
||||
---
|
||||
|
||||
## Total Task Summary
|
||||
|
||||
| Phase | Tasks | Story |
|
||||
|-------|-------|-------|
|
||||
| Phase 1 | 5 | Setup |
|
||||
| Phase 2 | 8 | Foundational |
|
||||
| Phase 3 | 10 | US1 |
|
||||
| Phase 4 | 10 | US2 |
|
||||
| Phase 5 | 9 | US3 |
|
||||
| Phase 6 | 8 | US4 |
|
||||
| Phase 7 | 10 | US5 |
|
||||
| Phase 8 | 5 | US6 |
|
||||
| Phase 9 | 17 | Polish |
|
||||
| **Total** | **82** | - |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Execute Phase 1-2**: Setup and entities
|
||||
2. **Run `/speckit-analyze`**: Validate cross-artifact consistency
|
||||
3. **Implement incrementally**: Start with MVP (Phases 1-3 + minimal Phase 9)
|
||||
4. **Test independently**: Each user story should be independently testable
|
||||
|
||||
---
|
||||
|
||||
**Ready for implementation** ✅
|
||||
Reference in New Issue
Block a user