8.2 KiB
eTMF Module Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Build an eTMF module that combines a compliant TMF directory tree with reusable document version management.
Architecture: Add eTMF directory nodes as metadata, then mount existing documents records onto those nodes with etmf_node_id. Keep file storage, versions, permissions, downloads, distribution, and audit behavior in the existing document module.
Tech Stack: FastAPI, SQLAlchemy async ORM, Alembic, PostgreSQL, Vue 3, Element Plus, Vitest, Pytest.
Implementation Notes
The repository instruction says not to plan or execute git commits unless explicitly requested. This plan intentionally omits commit steps.
Follow TDD for each backend behavior before implementation. Keep the first release small: no e-signature, no workflow redesign, no template versioning, no bulk import.
Task 1: Backend eTMF Node Model
Files:
- Create:
backend/app/models/etmf.py - Create:
backend/app/schemas/etmf.py - Modify:
backend/app/db/base.py - Create:
backend/alembic/versions/20260527_07_add_etmf_nodes.py - Test:
backend/tests/test_etmf_nodes.py
Step 1: Write failing model/import test
Add a test that imports EtmfNode through app.db.base metadata and verifies the table name is registered.
Run:
cd backend
pytest tests/test_etmf_nodes.py -q
Expected: fail because app.models.etmf does not exist.
Step 2: Add model
Create EtmfNode with these columns:
idstudy_idparent_idcodenamedescriptionscope_typerequiredexpected_doc_typesort_orderis_activecreated_atupdated_at
Use existing enum values from document scope where practical. Keep node-specific logic out of the model.
Step 3: Add schemas
Create request and response schemas:
EtmfNodeCreateEtmfNodeUpdateEtmfNodeReadEtmfTreeNodeEtmfNodeStatusSummary
Step 4: Add migration
Create etmf_nodes with:
- Foreign key to
studies.id - Self foreign key
parent_id - Unique constraint on
(study_id, parent_id, code) - Indexes on
study_id,parent_id,scope_type,is_active
Step 5: Verify
Run:
cd backend
pytest tests/test_etmf_nodes.py -q
Expected: pass.
Task 2: Link Documents To eTMF Nodes
Files:
- Modify:
backend/app/models/document.py - Modify:
backend/app/schemas/document.py - Modify:
backend/app/crud/document.py - Modify:
backend/app/services/document_service.py - Modify:
frontend/src/types/documents.ts - Modify:
backend/alembic/versions/20260527_07_add_etmf_nodes.py - Test:
backend/tests/test_etmf_nodes.py
Step 1: Write failing service test
Test that creating a document with etmf_node_id persists the relationship and list/detail responses include it.
Expected: fail because schema and model do not include etmf_node_id.
Step 2: Add model column
Add nullable Document.etmf_node_id with foreign key to etmf_nodes.id.
Step 3: Add schema fields
Add etmf_node_id to:
DocumentCreateDocumentUpdateDocumentSummaryDocumentDetail
Step 4: Validate node scope
In document_service.create_document, if etmf_node_id is provided:
- Verify node exists.
- Verify node belongs to the same study.
- If node scope is
SITE, requiresite_id. - If node scope is
GLOBAL, normalizesite_idto null unless the product decision changes.
Step 5: Verify
Run:
cd backend
pytest tests/test_etmf_nodes.py -q
Expected: pass.
Task 3: eTMF Service And Tree API
Files:
- Create:
backend/app/crud/etmf.py - Create:
backend/app/services/etmf_service.py - Create:
backend/app/api/v1/etmf.py - Modify:
backend/app/api/v1/router.py - Test:
backend/tests/test_etmf_api.py
Step 1: Write failing API tests
Cover:
GET /api/v1/etmf/tree?study_id=...- Required empty node returns
MISSING. - Node with document but no effective version returns
UPLOADED. - Node with current effective version returns
EFFECTIVE. - Site-scoped node can return per-site status.
Expected: fail because API does not exist.
Step 2: Implement CRUD
Add small CRUD functions:
get_nodelist_nodes_by_studycreate_nodeupdate_node
Avoid broad generic repository abstractions.
Step 3: Implement service
Add tree assembly and status calculation:
- Build parent-child tree in memory.
- Query documents for target study and group by
etmf_node_id. - Calculate node status from required flag and document effective version presence.
- Keep status calculation deterministic and isolated for unit tests.
Step 4: Implement routes
Add:
GET /treeGET /nodes/{node_id}/documentsPOST /nodesPATCH /nodes/{node_id}POST /nodes/{node_id}/documents
Use existing project permission checks through the document permission model.
Step 5: Verify
Run:
cd backend
pytest tests/test_etmf_api.py -q
Expected: pass.
Task 4: Frontend API And Types
Files:
- Create:
frontend/src/types/etmf.ts - Create:
frontend/src/api/etmf.ts - Test:
frontend/src/api/etmf.test.ts
Step 1: Write failing API test
Test that each frontend API helper calls the expected URL and HTTP method.
Expected: fail because src/api/etmf.ts does not exist.
Step 2: Add types
Add types matching backend schemas:
EtmfNodeStatusEtmfTreeNodeEtmfNodeCreatePayloadEtmfNodeUpdatePayload
Step 3: Add API helpers
Add:
fetchEtmfTreefetchEtmfNodeDocumentscreateEtmfNodeupdateEtmfNodecreateEtmfDocument
Step 4: Verify
Run:
cd frontend
npm test -- src/api/etmf.test.ts
Expected: pass.
Task 5: eTMF Page
Files:
- Replace:
frontend/src/views/ia/EtmfPlaceholder.vue - Modify:
frontend/src/locales/zh-CN.ts
Step 1: Write failing page test
Test that /etmf page:
- Renders tree panel.
- Loads eTMF tree for current study.
- Selecting a node loads its documents.
- Shows missing/effective status labels.
Expected: fail because the page is still a placeholder.
Step 2: Build layout
Use existing CTMS shell classes:
- Top action bar.
- Left tree panel.
- Main document table.
- Right node detail panel.
Do not introduce a marketing-style page or nested card layout.
Step 3: Add interactions
Implement:
- Center filter.
- Node selection.
- Refresh.
- Create node dialog.
- Create document dialog that posts to eTMF node document API.
Reuse existing display helpers and document enums.
Step 4: Verify
Run:
cd frontend
npm test -- src/api/etmf.test.ts
Expected: pass.
Task 6: Route And Permission Regression
Files:
- Modify:
frontend/src/utils/projectRoutePermissions.ts - Modify:
frontend/src/utils/projectRoutePermissions.test.ts - Modify:
backend/tests/test_api_permissions.py
Step 1: Write failing regression tests
Ensure:
/etmfstill requiresdocuments:read.- eTMF write endpoints are covered by document permissions.
- Existing document permission tests remain stable.
Step 2: Implement minimal permission wiring
Keep first-stage eTMF permissions mapped to existing document operation keys.
Step 3: Verify
Run:
cd backend
pytest tests/test_api_permissions.py -q
cd ../frontend
npm test -- src/utils/projectRoutePermissions.test.ts
Expected: pass.
Task 7: End-To-End Verification
Files:
- No source changes expected unless verification finds a bug.
Step 1: Run backend focused tests
cd backend
pytest tests/test_etmf_nodes.py tests/test_etmf_api.py tests/test_api_permissions.py -q
Expected: pass.
Step 2: Run frontend focused tests
cd frontend
npm test -- src/api/etmf.test.ts src/utils/projectRoutePermissions.test.ts
Expected: pass.
Step 3: Run UI contract check if available
cd frontend
npm run verify-ui-contract
Expected: pass.
Step 4: Manual browser check
Start the dev server and open /etmf with an active study:
cd frontend
npm run dev
Expected:
- eTMF page is not blank.
- Directory tree is visible.
- Selecting a node updates the document list.
- Center filter does not break layout.
- Text does not overlap at desktop and mobile widths.