material_manage_system/docs/superpowers/plans/2026-09-20-obsolete-material-policy.md
2026-09-20 14:29:41 +08:00

82 lines
4.7 KiB
Markdown

# Obsolete Material Policy Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Exclude obsolete materials from operational selection and reject them in new or edited warehouse operations while preserving administrative management, historical records, and readable disabled selections when editing old documents.
**Architecture:** `GET /materials` gains a validated status scope that defaults to active records. The material-management UI requests all records, while shared operational options keep the active default. A distinct active-status check is applied at server-side operation boundaries; historical queries remain status-agnostic.
**Tech Stack:** Go 1.21, Gin, MySQL, Vue 3, Pinia, Vitest, sqlmock.
**Spec:** User-approved table in this conversation: operational selectors return active materials; material management returns all; historical documents remain readable; new and edited operations require active materials.
## Global Constraints
- Do not delete or mutate historical document records.
- Keep `Exists` as an existence check; use a new active-status check for operational validation.
- Default list scope is `active`; only material management requests `all`.
- Use test-first development for each behavior change.
---
### Task 1: Status-scoped material lists
**Files:**
- Modify: `internal/handler/api.go`, `internal/service/material.go`, `internal/repository/material.go`
- Modify: `internal/repository/material_test.go`
- Modify: `frontend/src/stores/materials.js`, `frontend/src/views/Materials.vue`, `docs/api.md`
**Interfaces:**
- Produces: `MaterialService.List(search, status string, limit, offset int)` and repository equivalent.
- Produces: `GET /materials` defaults to `status=active`; `status=all` returns active and obsolete records.
- [ ] Write sqlmock tests for active and all list SQL predicates.
- [ ] Run the focused repository tests and observe missing status-aware query behavior.
- [ ] Add a `status` filter to the count and result queries; accept `active`, `obsolete`, and `all` in the handler.
- [ ] Make `fetchMaterials` accept a status argument and have `Materials.vue` request `all`; leave `fetchMaterialOptions` on the active default.
- [ ] Update API documentation and run focused backend/frontend tests.
### Task 2: Server-side active material validation
**Files:**
- Modify: `internal/service/material.go`, `internal/service/errors.go`
- Modify: `internal/service/inbound.go`, `internal/service/outbound.go`, `internal/service/application.go`
- Modify: `internal/handler/api.go`, `internal/service/material_test.go`
**Interfaces:**
- Produces: `MaterialService.IsActive(id int64) (bool, error)` and `ErrMaterialObsolete`.
- Consumes: active check in inbound, outbound, and application create/edit validation.
- [ ] Write a failing service test for a material whose status is `obsolete`.
- [ ] Run it and observe that existence currently passes without evaluating status.
- [ ] Implement `IsActive` and use it before transaction work in inbound/outbound/application workflows.
- [ ] Map `ErrMaterialObsolete` to a clear 400 API response.
- [ ] Run related service/handler tests.
### Task 3: Cache and regression verification
**Files:**
- Modify: `frontend/src/stores/materials.js`, `frontend/src/stores/materials.test.js`
- Modify: `docs/api.md`
- [ ] Write a failing frontend store test proving obsolete/restore refreshes operational options.
- [ ] Refresh `materialOptions` after obsolete and restore calls.
- [ ] Run the full frontend test suite and `go test ./...`.
- [ ] Confirm historical document detail queries remain unfiltered by material status.
### Task 4: Read-only representation of obsolete historical selections
**Files:**
- Modify: `internal/model/model.go`, `internal/repository/inbound.go`, `internal/repository/outbound.go`
- Modify: `frontend/src/views/Inbound.vue`, `frontend/src/views/Outbound.vue`
- Modify: `frontend/src/views/Inbound.test.js`
**Interfaces:**
- Produces: inbound/outbound item responses that include the material status used by editing forms.
- Produces: selected obsolete items are kept in their edit-form options with `disabled: true`.
- [ ] Write a failing view test that opens a historical document containing an obsolete material and expects its code/name to remain visible in a disabled select option.
- [ ] Include `m.status` in document item detail queries and responses without filtering the historical join.
- [ ] Merge an obsolete item from the document detail into that edit row's options only; mark it disabled and do not add it to shared active options.
- [ ] Verify save remains blocked server-side by Task 2 when the old obsolete material is retained.