# Project Number Traceability Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add a standalone `project_no` field and preserve it from inbound document creation through inbound details, job details, and inventory master/detail records.
**Architecture:** The inbound master form is the single input point. The backend copies the master value to every inbound detail, SQL Server procedures carry the detail value into job details and inventory rows, and Java/MyBatis models expose the value consistently as `projectNo`. Archive tables receive the same column because current completion procedures archive with `INSERT ... SELECT *`.
**Tech Stack:** Vue 2 + Element UI, Spring Boot/Java 17, MyBatis XML, SQL Server, JUnit 5/Mockito/AssertJ
---
### Task 1: Lock down the project-number propagation contract
**Files:**
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/in/stockin/service/impl/InStockinServiceImplTest.java`
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/in/job/mapper/MydoJobMapperSqlTest.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/in/stockin/mapper/ProjectNoMapperSqlTest.java`
- [ ] **Step 1: Add a service test that verifies the master project number is copied to each inbound detail**
```java
saveDTO.setProjectNo("PRJ-001");
service.saveInStockinWithDetails(saveDTO);
assertThat(detailCaptor.getValue().getProjectNo()).isEqualTo("PRJ-001");
```
- [ ] **Step 2: Add mapper/procedure text tests for all required columns and transfers**
```java
assertThat(inStockinMapperXml).contains("project_no", "#{projectNo}");
assertThat(jobDetailMapperXml).contains("project_no", "#{projectNo}");
assertThat(stockMapperXml).contains("project_no", "#{projectNo}");
assertThat(projectMigrationSql)
.contains("in_stockin", "in_stockin_detail", "mydo_job_detail")
.contains("bas_stock", "bas_stock_detail");
```
- [ ] **Step 3: Run focused tests and verify they fail before implementation**
Run: `mvn -pl ruoyi-admin -Dtest=InStockinServiceImplTest,ProjectNoMapperSqlTest,MydoJobMapperSqlTest test`
Expected: FAIL because `projectNo` properties, SQL mappings, and migration do not exist yet.
### Task 2: Add the database migration and procedure propagation
**Files:**
- Create: `sql/20260714_project_no_traceability.sql`
- Modify: `sql/proc_add_in_job_trace.sql`
- Modify: `sql/bas_stock_master_detail.sql`
- [ ] **Step 1: Add nullable project-number columns idempotently**
```sql
IF COL_LENGTH(N'dbo.in_stockin', N'project_no') IS NULL
ALTER TABLE dbo.in_stockin ADD project_no nvarchar(80) NULL;
```
Repeat for `in_stockin_detail`, `in_stockin_finish`, `in_stockin_detail_finish`, `mydo_job_detail`, `mydo_job_detail_finish`, `bas_stock`, and `bas_stock_detail`.
- [ ] **Step 2: Backfill existing traceable rows**
```sql
UPDATE d
SET d.project_no = h.project_no
FROM dbo.in_stockin_detail d
JOIN dbo.in_stockin h ON h.stockin_id = d.stockin_id
WHERE d.project_no IS NULL AND h.project_no IS NOT NULL;
```
Backfill job details from inbound details, inventory details from job/inbound details, and inventory masters from their inventory details.
- [ ] **Step 3: Update `proc_add_in_job` to copy `in_stockin_detail.project_no` into `mydo_job_detail.project_no`**
```sql
SELECT ..., d.project_no
FROM dbo.in_tray_msg tm
LEFT JOIN dbo.in_stockin_detail d ON tm.stockin_detail_id = d.stockin_detail_id;
INSERT INTO dbo.mydo_job_detail (..., project_no)
VALUES (..., @project_no);
```
- [ ] **Step 4: Update `proc_complete_in_job` to copy the job detail project number into inventory master and detail rows**
```sql
INSERT INTO bas_stock (..., project_no)
VALUES (..., @project_no);
INSERT INTO bas_stock_detail (..., project_no)
VALUES (..., @project_no);
```
- [ ] **Step 5: Keep baseline procedure scripts consistent with the deployable migration**
Update the existing standalone procedure scripts with the same cursor variables, selected columns, and insert columns.
### Task 3: Expose `projectNo` in backend entities and MyBatis mappings
**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/stockin/domain/InStockin.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/sotckindetail/domain/InStockinDetail.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/jobdetail/domain/MydoJobDetail.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/bas/stock/domain/BasStock.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/bas/stock/domain/BasStockDetail.java`
- Modify: `ruoyi-admin/src/main/resources/mapper/in/InStockinMapper.xml`
- Modify: `ruoyi-admin/src/main/resources/mapper/in/InStockinDetailMapper.xml`
- Modify: `ruoyi-admin/src/main/resources/mapper/in/MydoJobDetailMapper.xml`
- Modify: `ruoyi-admin/src/main/resources/mapper/bas/BasStockMapper.xml`
- Modify: `ruoyi-admin/src/main/resources/mapper/bas/BasStockDetailMapper.xml`
- [ ] **Step 1: Add the Java property to all five domain models**
```java
/** 项目号 */
@Excel(name = "项目号")
private String projectNo;
public String getProjectNo() { return projectNo; }
public void setProjectNo(String projectNo) { this.projectNo = projectNo; }
```
- [ ] **Step 2: Add `project_no` to every result map, select list, query filter, insert, and update section**
```xml
project_no,
#{projectNo},
```
- [ ] **Step 3: Run mapper contract tests**
Run: `mvn -pl ruoyi-admin -Dtest=ProjectNoMapperSqlTest test`
Expected: PASS.
### Task 4: Copy the master project number to inbound details and support the create form
**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/stockin/service/impl/InStockinServiceImpl.java`
- Modify: `ruoyi-ui/src/views/in/add/index.vue`
- [ ] **Step 1: Enforce server-side master-to-detail copying**
```java
detail.setStockinId(saveDTO.getStockinId());
detail.setProjectNo(saveDTO.getProjectNo());
```
- [ ] **Step 2: Add the project-number input to the main inbound form**
```vue
```
- [ ] **Step 3: Include `projectNo` in the form model and detail payload**
```javascript
mainForm: { projectNo: null }
// detail DTO
projectNo: this.mainForm.projectNo
```
- [ ] **Step 4: Run the inbound service test**
Run: `mvn -pl ruoyi-admin -Dtest=InStockinServiceImplTest test`
Expected: PASS and the captured detail contains the master `projectNo`.
### Task 5: Preserve project number when jobs are created from existing inventory
**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/location/service/impl/LocationOutboundServiceImpl.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/pd/stockpd/service/impl/PdStockpdServiceImpl.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java`
- [ ] **Step 1: Copy inventory-detail project numbers into outbound and inventory job details**
```java
jobDetail.setProjectNo(stockDetail.getProjectNo());
```
- [ ] **Step 2: Include `projectNo` in inventory material map queries used for return jobs and copy it into return-job details**
```java
returnDetail.setProjectNo(mapString(material, "projectNo"));
```
- [ ] **Step 3: Run focused service tests**
Run: `mvn -pl ruoyi-admin -Dtest=LocationOutboundServiceImplTest,PdStockpdServiceImplTest,PdaServiceImplTest test`
Expected: PASS.
### Task 6: Verify the end-to-end change
**Files:**
- Verify: all files listed above
- [ ] **Step 1: Run all backend tests**
Run: `mvn -pl ruoyi-admin test`
Expected: BUILD SUCCESS.
- [ ] **Step 2: Build the frontend production bundle**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: Build completes successfully.
- [ ] **Step 3: Inspect the final diff for unrelated or overwritten work**
Run: `git diff --check && git status --short`
Expected: no whitespace errors; existing user changes remain intact.