260429:1652 update Infras #07
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Tasks: Transmittals + Circulation Complete Integration (v1.8.7 Post-ADR-021)
|
||||
# Tasks: Transmittals + Circulation Complete Integration (v1.8.8 with Revision Refactor)
|
||||
|
||||
**Branch**: `001-transmittals-circulation` | **Total Tasks**: 18 | **Phase**: ✅ Complete (v1.8.7)
|
||||
**Branch**: `001-transmittals-circulation` | **Total Tasks**: 36 (18 v1.8.7 + 18 v1.8.8 Phase 4) | **Phase**: Phase 4 Ready — Revision Refactor
|
||||
|
||||
---
|
||||
|
||||
@@ -180,3 +180,168 @@ feat(frontend): wire WorkflowLifecycle + overdue badge in circulation detail
|
||||
test(transmittal): EC-RFA-004 submit validation unit tests
|
||||
test(circulation): EC-CIRC-001/002/003 edge case unit tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Transmittal Revision Refactor (v1.8.8)
|
||||
|
||||
**Based on**: Clarifications Session 2026-04-29
|
||||
**Goal**: Restructure Transmittal to follow Master-Revision Pattern
|
||||
|
||||
### R1 — Schema: Add `revision_id` to `transmittal_items`
|
||||
- **File**: `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql`
|
||||
- **Action**: Add `revision_id INT NULL` column with FK to `correspondence_revisions(id)`, create index per ADR-009
|
||||
- **Dependencies**: none
|
||||
- **Status**: [ ]
|
||||
|
||||
### R2 — Backend Entity: Update `TransmittalItem` with `revisionId`
|
||||
- **File**: `backend/src/modules/transmittal/entities/transmittal-item.entity.ts`
|
||||
- **Action**: Add `revisionId` column (nullable), add `@ManyToOne` relation to `CorrespondenceRevision`
|
||||
- **Dependencies**: R1
|
||||
- **Status**: [ ]
|
||||
|
||||
### R3 — Backend Service: Update `findOneByUuid` to read from revision
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: Join `correspondence_revisions`, read `purpose`/`remarks` from `details` JSON field; include `revisionId`, `revisionNumber`, `revisionLabel` in response
|
||||
- **Dependencies**: R2
|
||||
- **Status**: [ ]
|
||||
|
||||
### R4 — Backend Service: Add `createRevision()` method
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: Create new `correspondence_revisions` record, copy all items from current revision to new revision via `copyItemsToRevision()` helper
|
||||
- **Dependencies**: R3
|
||||
- **Status**: [ ]
|
||||
|
||||
### R5 — Backend Service: Add `copyItemsToRevision()` helper
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: Clone all `transmittal_items` where `revision_id = oldRevisionId`, insert new records with `revision_id = newRevisionId`. **Success Criteria**: (1) Item count in new revision equals old revision, (2) All `quantity` values preserved, (3) `item_correspondence_id` FK constraints pass, (4) Atomic transaction (rollback on failure).
|
||||
- **Dependencies**: R2
|
||||
- **Status**: [ ]
|
||||
|
||||
### R6 — Backend Controller: Add `POST /:uuid/revisions` endpoint
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.controller.ts`
|
||||
- **Action**: New endpoint with `@RequirePermission('document.manage')` (ADR-016), `@Audit('transmittal.create-revision', 'transmittal')`, calls `createRevision()`, returns `{ revisionId, revisionNumber, revisionLabel }`
|
||||
- **Dependencies**: R4
|
||||
- **Status**: [ ]
|
||||
- **Security**: CASL Guard required — Document Control role or above
|
||||
|
||||
### R7 — Backend Service: Update `submit()` for revision-scoped items
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: EC-RFA-004 validation checks items for current revision only; workflow instance binds to `correspondence_revisions.id`
|
||||
- **Dependencies**: R3, R4
|
||||
- **Status**: [ ]
|
||||
|
||||
### R8 — Frontend Types: Add revision fields to `Transmittal`
|
||||
- **File**: `frontend/types/transmittal.ts`
|
||||
- **Action**: Add `revisionId?: string`, `revisionNumber?: number`, `revisionLabel?: string` to `Transmittal` interface
|
||||
- **Dependencies**: none (parallel)
|
||||
- **Status**: [ ]
|
||||
|
||||
### R9 — Frontend Types: Add `revisionId` to `TransmittalItem`
|
||||
- **File**: `frontend/types/transmittal.ts`
|
||||
- **Action**: Add `revisionId?: string` to `TransmittalItem` interface
|
||||
- **Dependencies**: R8
|
||||
- **Status**: [ ]
|
||||
|
||||
### R10 — Frontend Page: Add revision selector to detail page
|
||||
- **File**: `frontend/app/(dashboard)/transmittals/[uuid]/page.tsx`
|
||||
- **Action**: Show revision dropdown when multiple revisions exist (like RFA pattern), display `revisionLabel` (A, B, C) in banner
|
||||
- **Dependencies**: R8, R9
|
||||
- **Status**: [ ]
|
||||
|
||||
### R11 — Frontend Hook: Update `useTransmittal` for revision context
|
||||
- **File**: `frontend/hooks/use-transmittal.ts`
|
||||
- **Action**: Add optional `revisionId` parameter to fetch specific revision; default to current revision
|
||||
- **Dependencies**: R8
|
||||
- **Status**: [ ]
|
||||
|
||||
### R12 — Workflow Engine: Update `getInstanceByEntity` for revision binding (ADR-019)
|
||||
- **File**: `backend/src/modules/workflow-engine/workflow-engine.service.ts`
|
||||
- **Action**: Support `entity_type='transmittal'` with `entity_id=revision.publicId` (UUID string, NOT revision.id INT). Ensure workflow instance stores and retrieves using UUIDv7 string per ADR-019.
|
||||
- **Dependencies**: R3
|
||||
- **Status**: [ ]
|
||||
- **ADR-019 Check**: Use `revision.publicId` (string) — never `revision.id` (INT) for entity binding
|
||||
|
||||
### R14 — Backend Service: Update `create()` to write `purpose`/`remarks` to `details` JSON
|
||||
- **File**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: In `create()`, stop writing `purpose`/`remarks` to `Transmittal` entity; instead store them in `CorrespondenceRevision.details = { purpose, remarks }` JSON field. Remove `purpose`/`remarks` from `queryRunner.manager.create(Transmittal, {...})` call.
|
||||
- **Dependencies**: R3 (findOneByUuid reads from details)
|
||||
- **Status**: [ ]
|
||||
- **Note**: Must deploy BEFORE step 3 SQL (DROP COLUMN) in schema-02-tables.sql
|
||||
|
||||
### R15 — Schema: Drop `purpose` and `remarks` from `transmittals` table
|
||||
- **File**: `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql`
|
||||
- **Action**: `ALTER TABLE transmittals DROP COLUMN purpose, DROP COLUMN remarks;` per ADR-009. Also remove corresponding TypeORM columns from `transmittal.entity.ts`.
|
||||
- **Dependencies**: R14 (must be fully deployed first)
|
||||
- **Status**: [ ]
|
||||
- **ADR-009**: Direct SQL only — no TypeORM migration file
|
||||
|
||||
### R16 — DTO: Fix `TransmittalItemDto.itemId` to UUID (ADR-019)
|
||||
- **File**: `backend/src/modules/transmittal/dto/create-transmittal.dto.ts` + `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: Change `itemId: number` + `@IsInt()` → `itemId: string` + `@IsUUID()`. In `create()`, replace direct assignment with `uuidResolver.resolveCorrespondenceId(item.itemId)` before saving `itemCorrespondenceId`.
|
||||
- **Dependencies**: R1 (schema must be stable)
|
||||
- **Status**: [ ]
|
||||
- **ADR-019**: CRITICAL — Frontend must send `publicId` (UUID string), not INT id
|
||||
|
||||
### R17 — Schema + Entity: Add `itemType` column to `transmittal_items` (H1)
|
||||
- **Files**: `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` + `backend/src/modules/transmittal/entities/transmittal-item.entity.ts` + `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: (1) SQL: `ALTER TABLE transmittal_items ADD COLUMN item_type VARCHAR(50) NULL COMMENT 'ประเภทเอกสาร เช่น DRAWING, RFA, CORRESPONDENCE' AFTER item_correspondence_id;` (ADR-009). (2) Entity: add `@Column({ name: 'item_type', nullable: true }) itemType?: string;`. (3) Service `create()`: save `itemType: item.itemType` from DTO (field already exists in `TransmittalItemDto`).
|
||||
- **Dependencies**: R1
|
||||
- **Status**: [ ]
|
||||
- **Note**: Fixes H1 — DTO had `itemType` but it was never persisted to DB
|
||||
|
||||
### R18 — Service: Fix `ORG_CODE` hardcode in `create()` (M1)
|
||||
- **Files**: `backend/src/modules/transmittal/transmittal.service.ts`
|
||||
- **Action**: Before `generateNextNumber()`, fetch originator org: `const originatorOrg = await this.dataSource.manager.findOne(Organization, { where: { id: userOrgId } }); const orgCode = originatorOrg?.organizationCode ?? 'UNK';` — then replace `ORG_CODE: 'ORG'` with `ORG_CODE: orgCode`. Pattern matches `correspondence.service.ts` line 263-269.
|
||||
- **Dependencies**: none (parallel)
|
||||
- **Status**: [ ]
|
||||
- **Note**: Fixes M1 — `Organization.organizationCode` field confirmed at `organization.entity.ts:24`
|
||||
|
||||
### R13 — Validation: ADR-019 UUID Compliance Check
|
||||
- **File**: `backend/src/modules/transmittal/` + `frontend/types/transmittal.ts`
|
||||
- **Action**: Verify all revision-related fields use `publicId` (string UUID) not `id` (INT): `revisionId`, `workflowInstanceId`, `transmittalId` in responses. Run `grep -n "parseInt\|Number(\|\.id[^a-zA-Z]"` to catch violations.
|
||||
- **Dependencies**: R2, R3, R12
|
||||
- **Status**: [ ]
|
||||
- **ADR-019**: CRITICAL — Zero tolerance for INT ID exposure in API responses
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 Execution Order
|
||||
|
||||
```
|
||||
R1 (schema)
|
||||
→ R2 (entity)
|
||||
→ R3 (service findOneByUuid) ─┬→ R4 (createRevision) → R6 (controller endpoint)
|
||||
│ → R5 (copyItems helper)
|
||||
├→ R7 (submit update)
|
||||
├→ R12 (workflow binding update)
|
||||
└→ R13 (ADR-019 validation)
|
||||
R8 (frontend types) ─┬→ R9 (item types)
|
||||
→ R11 (hook update) │
|
||||
→ R10 (page update) ─┘
|
||||
R3 → R14 (create() writes to details JSON)
|
||||
→ R15 (DROP COLUMN purpose/remarks) ← deploy R14 first
|
||||
R1 → R17 (add item_type column + entity + save in create())
|
||||
R18 (fix ORG_CODE — no dependencies, parallel safe)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 Commit Message Convention
|
||||
|
||||
```
|
||||
feat(schema): add revision_id to transmittal_items table (ADR-009)
|
||||
feat(transmittal): add revisionId column to TransmittalItem entity
|
||||
feat(transmittal): update findOneByUuid to read from correspondence_revisions
|
||||
feat(transmittal): add createRevision with automatic item copying
|
||||
feat(transmittal): add copyItemsToRevision helper method
|
||||
feat(transmittal): add POST /:uuid/revisions endpoint
|
||||
feat(transmittal): update submit for revision-scoped items (EC-RFA-004)
|
||||
feat(frontend): add revision fields to Transmittal types
|
||||
feat(frontend): add revision selector to transmittal detail page
|
||||
feat(workflow-engine): update getInstanceByEntity for revision binding
|
||||
chore(validation): ADR-019 UUID compliance check for revision refactor
|
||||
fix(transmittal): change TransmittalItemDto.itemId from INT to UUID string (ADR-019)
|
||||
feat(transmittal): add item_type column to transmittal_items and persist from DTO (H1)
|
||||
fix(transmittal): replace hardcoded ORG_CODE with real organizationCode lookup (M1)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user