ForgeFlow-ERP/docs/superpowers/plans/2026-06-13-warehouse-paper-form-unification-phase1.md
2026-06-14 16:23:27 +08:00

213 lines
9.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Warehouse Paper Form Unification Phase 1 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:** Make warehouse operation documents use one cohesive paper-form visual language, starting with the production-ledger controlled inbound forms that currently mix modern web controls into the paper sheet.
**Architecture:** Keep all business state, computed values, validation, and submit payloads in `InventoryLedgerView.vue` unchanged. Introduce paper-specific wrappers/classes around the existing controls so selectors, selected-lot tables, live summaries, remarks, and settle controls visually belong to the same document grid as the approved “客料入库单”. Avoid global restyling of `.form-field`, `.double-field`, `.table-wrap`, or shared controls used outside paper documents.
**Tech Stack:** Vue 3 SFC, Vite, project CSS in `frontend/src/styles/main.css`, Node built-in test runner.
---
## File Structure
- Modify `frontend/src/views/InventoryLedgerView.vue`: convert the generic warehouse drawers production-ledger controlled inbound branch to paper-specific markup while preserving existing bindings and branch conditions.
- Modify `frontend/src/styles/main.css`: add reusable paper document classes for control grids, inline summary rows, source-lot selector/table panels, and settle confirmation rows.
- Modify `frontend/src/views/InventoryLedgerView.test.js`: add regression coverage that production surplus/scrap inbound forms use the paper-specific classes and that the old naked modern classes are not the only visual contract.
## Design Direction
- Aesthetic: “工业纸质单据”.
- Purpose: Support fast warehouse entry while making each operation feel like a formal business document, not a demo web form.
- Differentiation anchor: Thick ledger grid lines, label/value cells, stamped meta strips, and paper-native control panels.
- DFII: 13. Impact 4, fit 5, feasibility 5, performance 4, consistency risk 5. This is strong because it reuses the approved客料入库 visual system, but must be scoped to avoid touching unrelated app forms.
## Task 1: Lock The Regression Tests
**Files:**
- Modify: `frontend/src/views/InventoryLedgerView.test.js`
- Read: `frontend/src/views/InventoryLedgerView.vue`
- Read: `frontend/src/styles/main.css`
- [ ] **Step 1: Add paper-form assertions for production-ledger controlled inbound**
Add a test that checks the generic drawer contains production-ledger paper classes and keeps the old branch visually wrapped:
```js
it("wraps production-ledger controlled inbound forms in the shared paper control language", () => {
assert.match(source, /warehouse-paper-control-grid/);
assert.match(source, /warehouse-paper-source-lot-panel/);
assert.match(source, /warehouse-paper-source-lot-table/);
assert.match(source, /warehouse-paper-inline-summary/);
assert.match(source, /warehouse-paper-settle-panel/);
assert.match(source, /warehouse-paper-settle-toggle/);
assert.match(source, /warehouse-paper-settle-grid/);
assert.match(source, /warehouse-paper-settle-remark/);
assert.match(mainCss, /\.warehouse-document-section-grid > \.warehouse-paper-control-grid\s*\{[\s\S]*grid-column: 1 \/ -1;/);
assert.match(mainCss, /\.warehouse-paper-control-grid\s*\{[\s\S]*grid-template-columns: repeat\(4, minmax\(0, 1fr\)\);/);
assert.match(mainCss, /\.warehouse-paper-source-lot-table/);
assert.match(mainCss, /\.warehouse-paper-settle-toggle/);
});
```
- [ ] **Step 2: Run the test and confirm it fails before implementation**
Run:
```bash
cd frontend
node --test src/views/InventoryLedgerView.test.js
```
Expected before implementation: failure mentioning one or more missing `warehouse-paper-*` classes.
## Task 2: Convert Production Controlled Inbound Markup
**Files:**
- Modify: `frontend/src/views/InventoryLedgerView.vue`
- [ ] **Step 1: Replace the naked production work-order block**
Replace the `isProductionWorkOrderControlledInbound` `stack-form` block with paper-grid fields:
```vue
<div v-if="isProductionWorkOrderControlledInbound" class="warehouse-paper-control-grid warehouse-paper-control-grid-compact">
<label class="warehouse-paper-field warehouse-paper-field-wide">
<span>{{ isReworkScrapInbound ? "返工工单" : "生产台账" }}</span>
<select v-model.number="genericForm.work_order_id" required @change="applyGenericWorkOrder">
<option disabled value="">{{ isReworkScrapInbound ? "请选择返工工单" : "请选择生产台账" }}</option>
<option v-for="workOrder in genericWorkOrderOptions" :key="workOrder.work_order_id" :value="workOrder.work_order_id">
{{ formatWorkOrderLedgerOption(workOrder, activeOperation?.bizType) }}
</option>
</select>
</label>
<template v-if="selectedGenericWorkOrder">
<article class="warehouse-paper-summary-card warehouse-paper-summary-card-wide">
<span>产品</span>
<strong>{{ selectedGenericWorkOrder.product_code }} · {{ selectedGenericWorkOrder.product_name }}</strong>
</article>
<article class="warehouse-paper-summary-card">
<span>材料库存批次号</span>
<strong>{{ selectedGenericWorkOrder.source_lot_summary || selectedGenericWorkOrder.material_lot_no || "-" }}</strong>
</article>
<article v-if="isProductionScrapInbound" class="warehouse-paper-summary-card">
<span>库外未闭环重量</span>
<strong>{{ formatWeight(selectedGenericWorkOrder.outside_weight_kg || selectedGenericWorkOrder.limits?.max_scrap_inbound_weight_kg) }}</strong>
</article>
<article v-if="isProductionSurplusInbound" class="warehouse-paper-summary-card">
<span>库外未闭环重量</span>
<strong>{{ formatWeight(selectedGenericWorkOrder.outside_weight_kg || selectedGenericWorkOrder.limits?.max_surplus_inbound_weight_kg) }}</strong>
</article>
</template>
</div>
```
- [ ] **Step 2: Convert the core fields to a paper grid**
Replace the main `double-field` and `triple-field` groups in the generic drawer with `warehouse-paper-control-grid`, using the existing exact conditions and v-models. Keep field order: item, warehouse, location, measure basis, weight, quantity, unit cost.
- [ ] **Step 3: Wrap source-lot selector and tables in paper panels**
Keep `StockLotTagSelect` unchanged, but place it inside:
```vue
<div v-if="usesGenericSourceLotSelector" class="warehouse-paper-source-lot-panel">
<div class="warehouse-paper-source-lot-heading">
<span>来源库存批次号</span>
<strong>{{ genericSourceLotSelectPlaceholder }}</strong>
</div>
<StockLotTagSelect ... />
</div>
```
For selected lot tables, keep the same columns and inputs but add `warehouse-paper-source-lot-table` to the wrapping table container so CSS can remove the ordinary web-card feel.
- [ ] **Step 4: Convert summaries and settle controls**
Replace `inventory-live-summary` display with `warehouse-paper-inline-summary`, and replace `work-order-settle-panel` / `work-order-settle-toggle` with paper-specific classes. Keep `genericForm.settle_work_order`, deviation values, and `genericForm.settle_remark` unchanged.
## Task 3: Add Paper-Specific CSS
**Files:**
- Modify: `frontend/src/styles/main.css`
- [ ] **Step 1: Add grid spanning rules**
Add the new paper containers beside existing `.warehouse-paper-grid` rules:
```css
.warehouse-document-section-grid > .warehouse-paper-control-grid,
.warehouse-document-section-grid > .warehouse-paper-source-lot-panel,
.warehouse-document-section-grid > .warehouse-paper-source-lot-table,
.warehouse-document-section-grid > .warehouse-paper-inline-summary,
.warehouse-document-section-grid > .warehouse-paper-settle-panel {
grid-column: 1 / -1;
}
```
- [ ] **Step 2: Reuse approved paper field and summary cells**
Make `.warehouse-paper-control-grid` visually match `.warehouse-paper-grid`, with four columns on desktop, two on tablet, and one on mobile. Make `.warehouse-paper-control-grid-compact` allow summary cards to live in the same grid.
- [ ] **Step 3: Style selector/table/settle panels as document rows**
Style:
```css
.warehouse-paper-source-lot-panel
.warehouse-paper-source-lot-heading
.warehouse-paper-source-lot-table
.warehouse-paper-inline-summary
.warehouse-paper-settle-panel
.warehouse-paper-settle-toggle
.warehouse-paper-settle-grid
.warehouse-paper-settle-remark
```
They should use document borders, paper backgrounds, Songti/Simsun value text where appropriate, and no floating modern-card shadows.
## Task 4: Verify
**Files:**
- Test: `frontend/src/views/InventoryLedgerView.test.js`
- Build: `frontend/package.json`
- [ ] **Step 1: Run focused view tests**
Run:
```bash
cd frontend
node --test src/views/InventoryLedgerView.test.js
```
Expected: all tests pass.
- [ ] **Step 2: Run production build**
Run:
```bash
cd frontend
npm run build
```
Expected: Vite build succeeds. Existing chunk-size warning is acceptable if it remains the same non-blocking warning.
- [ ] **Step 3: Browser verification**
Open `http://localhost:5173/inventory-ledger`, then inspect:
- 原材料库 · 客料入库 remains unchanged and approved.
- 原材料库 · 生产余料入库 uses the same paper-grid visual language.
- The source-lot multi-select, selected-lot table, remark field, and “该批材料余料结单” no longer look like separate modern web cards pasted onto paper.
- Existing submit button and drawer behavior still work.
## Self-Review
- Spec coverage: covers the users requested recommended approach by converting the most visible mixed-UI warehouse document first and creating reusable paper classes for subsequent warehouse forms.
- Placeholder scan: no TBD/TODO/implement-later placeholders.
- Type consistency: class names in tests, markup, and CSS use the same `warehouse-paper-*` prefix.