260429:1652 update Infras #07
CI / CD Pipeline / build (push) Successful in 5m14s
CI / CD Pipeline / deploy (push) Successful in 2m2s

This commit is contained in:
admin
2026-04-29 16:52:08 +07:00
parent de059c9edb
commit d239b58387
3 changed files with 304 additions and 6 deletions
+167 -2
View File
@@ -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)
```