新增入库单据管理页面

This commit is contained in:
zgw
2025-12-18 10:42:57 +08:00
parent bbe7b8a021
commit abf21af5a8
34 changed files with 3753 additions and 78 deletions

View File

@@ -0,0 +1,53 @@
## 问题判断
- 仅勾选一条子单仍打印两页,典型原因是分页规则对最后一页也生效或伪类匹配异常导致空白页。
- `:last-child` 在存在空白文本节点/注释节点的情况下可能失效,导致最后一个实际页面元素仍应用分页。
- 也可能存在重复数据(同一个 `stockinDetailId` 被选中两次),引发两页输出。
## 修复与优化方案
### 1. 明确分页控制(按索引条件渲染)
- 将分页控制从 CSS 伪类改为模板条件,避免 `:last-child` 因节点影响而失效:
- 模板改为:
```vue
<div
v-for="(item, idx) in printData"
:key="item.stockinDetailId"
class="print-page"
:style="{ pageBreakAfter: idx < printData.length - 1 ? 'always' : 'auto' }"
>
<!-- 原卡片内容保持不变 -->
</div>
```
### 2. 去重与防重复打印
- 在 `printDetailQRCodes` 中对 `detailSelections` 去重,确保同一子单只打印一次:
```js
const map = new Map()
this.printData = []
for (const row of this.detailSelections) {
const key = row.stockinDetailId
if (key && !map.has(key)) {
map.set(key, true)
this.printData.push(row)
}
}
```
### 3. 保证单页不被拆分
- 保留并强化:`.print-page { page-break-inside: avoid; }`,防止卡片高度导致跨页拆分。
- 控制卡片外层高度与内边距不超过 A4 可打印区域(当前 720px 宽 + 常规内容高度安全)。
### 4. 打印触发时机与副本数
- 维持 `this.$nextTick(() => setTimeout(() => this.$refs.easyPrint2.print(), 100))`,避免多次触发。
- 如仍出现两页,追加保护:打印前记录 `printData.length` 与内容,控制仅当 `length >= 1` 才触发一次。
## 代码变更摘要
- 更新模板:将分页规则改为基于索引的内联样式条件。
- 更新方法:`printDetailQRCodes` 增加去重逻辑与空数组保护。
- 保留样式:`page-break-inside: avoid`;移除或忽略 `:last-child` 分页伪类。
## 验证步骤
- 勾选 1 条子单打印,预览应仅 1 页。
- 勾选 2 条子单打印,预览应为 2 页且无末尾空白页。
- 检查不同浏览器(Chromium/Edge)表现一致性。
请确认按此方案实施,我将立即更新代码并验证。

View File

@@ -0,0 +1,104 @@
## 目标
* 在 SQL Server 新增表 `in_stockin_detail_n`,用于存储 ERP 返回的入库产品明细(Cols/Rows)。
* 以实体、Mapper、Service 的方式接入到现有 WMS 代码,并在 `ToErpServiceImpl#getInStock` 中解析 JSON 后落库。
* 字段覆盖现有 ERP 索引映射,支持来源、创建/更新时间等审计信息。
## 数据表设计(SQL Server)
* 表名:`in_stockin_detail_n`
* 关键字段:
* stockin\_detail\_id (入库单主键,插入时,主单对应有几条子单,在主单ID后拼接“-”子单条数)
* stockin\_id 主单ID 与主单ID保持一致
* `erp_detail_id`(ERP入库明细ID,对应 Rows\[0])
* `product_name` nvarchar(200)(Rows\[1])
* `product_model` nvarchar(200)(Rows\[2],型号)
* `unit` nvarchar(50)(Rows\[3])
* `unit_price` decimal(18,6)(Rows\[4])
* `aux_unit` nvarchar(50) 或 int(Rows\[5],与现有实现保持兼容;建议 nvarchar 以避免字典耦合)
* `attr1` nvarchar(100)(Rows\[6])
* `attr2` nvarchar(100)(Rows\[7])
* `quantity` decimal(18,6)(Rows\[9])
* `in_quantity` decimal(18,6)(Rows\[10])
* `aux_quantity` decimal(18,6)(Rows\[11])
* `total_price` decimal(18,6)(Rows\[12])
* `cost_unit_price` decimal(18,6)(Rows\[13])
* `cost_total_price` decimal(18,6)(Rows\[14])
* `accounting_cost_total` decimal(18,6)(Rows\[15])
* `accounting_quantity` decimal(18,6)(Rows\[16])
* `accounting_status` int(Rows\[17])
* `batch_no` nvarchar(100)(Rows\[18])
* `serial_no` nvarchar(100)(Rows\[19])
* `production_date` datetime2(3)(Rows\[20])
* `expiry_date` datetime2(3)(Rows\[21])
* `arrival_date` datetime2(3)(Rows\[22])
* `remark` nvarchar(255)(Rows\[23])
* `custom1`..`custom6` nvarchar(255)(Rows\[24]..Rows\[29])
* `stevedore` nvarchar(100)(Rows\[30],装卸工)
* `create_time` datetime2(3) 默认 `SYSDATETIME()`
* `update_time` datetime2(3) 可在更新时写入 `SYSDATETIME()`
* 约束:`detail_id` 主键;如需防重复插入,可使用 `detail_id + production_date` 唯一索引(按您确认)。
## Java 代码接入
* 新增实体 `com.ruoyi.system.domain.InStockinDetailN`
* 字段与表结构一一对应,类型与上面一致;保留必要的 `@Excel` 注解,遵循现有风格。
* 继承 `BaseEntity`,复用 `createTime`、`updateTime` 字段。
* 新增 Mapper 接口 `com.ruoyi.system.mapper.InStockinDetailNMapper`
* 方法:`insertInStockinDetailN`、`updateInStockinDetailN`、`selectByDetailId`、`batchInsert(List<...>)`、`selectByDetailIdAndDate`(防重复)。
* 新增 XML `mapper/system/InStockinDetailNMapper.xml`
* 定义 `resultMap` 与列映射;提供 `insert`、`update`、`select`、`batchInsert`(`foreach`)语句,时间字段默认使用 `SYSDATETIME()`。
* 新增 Service `IInStockinDetailNService` 与实现 `InStockinDetailNServiceImpl`
* 封装批量写入与防重复逻辑(按 `detail_id` 或当天 `create_time` 过滤)。
## 解析与入库逻辑
* 在 `ToErpServiceImpl#getInStock` 中:
* 从 `row.get(15)`(产品明细 JSON)解析出 `Rows` 列表。
* 按上面索引映射生成 `InStockinDetailN` 对象,填充 `order_src='ERP'`、`create_time=当前时间`。
* 通过 `selectByDetailIdAndDate(detail_id, today)` 判断是否已存在,未存在则插入,已存在忽略。
* 将 `stockin_guid` 关联为当前主单 `newStockin.getStockinGuid()`(若可获得)。
## 索引映射校验
* 结合已有 `ErpInStockinDetail` 的使用位置(`ToErpServiceImpl#AddStockinDetailByErpStockinDetail`),确保以下关键位一致:
* `id`→Rows\[0]、`spare1`(产品名称)→Rows\[1]、`spare2`(产品编号/型号)→Rows\[2]
* 数量→Rows\[9]、批号→Rows\[17]、序列号→Rows\[18]、生产/有效/到货日期→Rows\[19]/\[20]/\[21]
* 如您确认 `Clos` 与 `Rows` 存在偏移或后续附加列,我们将按您提供的最终结构调整表与映射。
## 验证与回归
* 本地运行 `getInStock`,抓取一次 ERP 返回;在控制台输出解析计数与首条样例,以确认列位。
* 手工查询:`SELECT TOP 10 * FROM in_stockin_detail_n ORDER BY create_time DESC;`
* 回归检查:对 `in_stockin_n` 与新表之间的 `stockin_guid` 关联是否正确。
## 提交约定
* 在实现时,所有 Java 代码将补充详细中文注释,解释每一步改动原因与映射关系。
* 不自动创建 `.md` 或 `.sql` 文件;如您需要,我们可生成对应 SQL 文件并在您确认后执行。
* 仅在您确认后进行代码与数据库变更,并按您要求的 Git 提交规范执行提交。

View File

@@ -0,0 +1,61 @@
## 目标
- 在 `InStockIn` 页面满足 5 项需求:
1. 点击“单据新增”时,打开弹框隐藏“打印入库条码”按钮
2. 弹框内新增“选择物料”按钮,打开第二个弹框显示物料列表,选择后将物料加入子单明细
3. 子单明细中的“计划数量”和“入库数量”可编辑
4. 新增时自动生成 `stockinId`:`RK` + 当前毫秒时间戳
5. 提交时 `orderSrc` 默认赋值为“自建单”
## 技术实现
- 页面文件:`ruoyi-ui/src/views/system/inStockIn/index.vue`
- 参考实现:`ruoyi-ui/src/views/in/stockIn/add/index_new.vue` 的“添加物料”与“选择物料”弹框逻辑
## 详细改动
### 新增状态标识
- 在 `data()` 中新增:`isAddMode: false`
- 用于区分“新增”与“编辑/查看”,控制按钮显示与表格可编辑
### 隐藏打印按钮
- 对话框底部的打印按钮(`printDetailQRCodes`)改为:`v-if="!isAddMode"`
- 点击“单据新增”时将 `isAddMode = true`,从而隐藏打印按钮
### 选择物料入口
- 在“子单信息”卡片标题区域新增一个按钮:`选择物料`
- 按钮事件 `openProductSelector()` 打开二级弹框
### 物料选择弹框
- 复用 `index_new.vue` 的模式:
- 引入 API:`import { listBasProduct } from "@/api/bas/BasProduct"`
- 在 `data()` 中新增:`productSelectVisible, productOptions, selectedProducts, productTotal, queryProductParams, productLoading`
- 弹框内容包含搜索表单、物料表格(带选择)、分页与“确定/取消”
- 事件:`getProductList()、resetProductQuery()、handleProductSelectionChange()、confirmSelectProduct()`
### 子单明细数据与可编辑列
- 子单明细列表数据源仍使用 `inStockinDetailNList`
- 在“新增”模式下,`confirmSelectProduct()` 将选中的物料映射并 `push` 到 `inStockinDetailNList`:
- 字段映射示例:`productName、productModel(来自 productId 或 spec)、unit、quantity(默认 1)、inQuantity(默认 0)`
- 在子单表格中为 `quantity` 与 `inQuantity` 列增加模板:`<el-input-number v-model="scope.row.quantity" ...>` 与 `<el-input-number v-model="scope.row.inQuantity" ...>`,并用 `v-if="isAddMode"` 控制仅在新增时可编辑
### 自动生成主单号
- 在 `handleAdd()`:
- `this.reset()` 后设置:`this.form.stockinId = 'RK' + Date.now()`
- `this.isAddMode = true; this.open = true; this.isView = false; this.title = '添加入库单据管理'`
- 由于 `watch('form.stockinId')` 会尝试拉取子单,新增生成的主单号在后端不存在,列表将为空,此时通过物料选择填充本地子单明细
### 提交默认来源
- 在 `submitForm()` 里,在发起 `addInStockIn(this.form)` 前设置:`this.form.orderSrc = '自建单'`
- 编辑场景保持原逻辑不变;新增成功后 `isAddMode` 复位为 `false`
## 校验与兼容
- 行内“修改/删除”按钮逻辑保持现有条件控制;新增模式下仅本地操作子单明细
- 不影响历史单据查询与打印
- 若后端需要接收子单明细,一并在后续迭代中按后端接口扩展(当前仅前端本地编辑)
## 代码触点
- 模板:对话框底部按钮处、子单卡片标题区新增按钮、子单表格新增可编辑列
- 脚本:新增 `isAddMode` 标识、导入 `listBasProduct`、新增弹框及方法、在 `handleAdd()` 和 `submitForm()` 中按要求赋值
## 期望交互
- 点击“新增”→ 弹框打开且不显示打印按钮;自动生成主单号
- 点“选择物料”→ 打开物料选择弹框,勾选并确定→ 子单明细填充,可编辑数量
- 点“确定”提交→ 主单 `orderSrc` 自动为“自建单”

View File

@@ -0,0 +1,55 @@
## 目标
- 为 `in_stockin_n_finish` 和 `in_stockin_detail_n_finish` 两张“完成/归档表”新增后端代码(Mapper 接口 + MyBatis XML + Service 接口与实现)。
- 字段结构与现有 `in_stockin_n`、`in_stockin_detail_n` 完全一致,仅更换表名与命名空间。
## 范围
- 复用现有领域模型:直接复用 `InStockinN` 与 `InStockinDetailN` 两个实体类,不新增冗余实体。
- 新增 Mapper 接口与 XML:Finish 版本分别指向 `_finish` 表名。
- 新增 Service 接口与实现:提供基本 CRUD 与“从原表批量归档到 finish 表”的能力。
- 不改动现有 Controller(如需页面展示 Finish 列表,可后续扩展)。
## 技术方案
- MyBatis:在 `mapper/system` 目录新增两个 XML 文件,`resultMap` 的 `type` 仍然使用现有实体类;`select`/`insert`/`update`/`delete` 语句的 `from`/`into` 指向 `_finish` 表。
- Mapper 接口:在 `com.ruoyi.system.mapper` 新增 `InStockinNFinishMapper`、`InStockinDetailNFinishMapper`,方法签名与现有 Mapper 保持一致(命名不同)。
- Service:在 `com.ruoyi.system.service` 新增 `IInStockinNFinishService`、`IInStockinDetailNFinishService`;在 `com.ruoyi.system.service.impl` 新增对应实现类,注入 Finish Mapper 完成操作。
## 文件清单
- `ruoyi-admin/src/main/java/com/ruoyi/system/mapper/InStockinNFinishMapper.java`
- `ruoyi-admin/src/main/java/com/ruoyi/system/mapper/InStockinDetailNFinishMapper.java`
- `ruoyi-admin/src/main/resources/mapper/system/InStockinNFinishMapper.xml`
- `ruoyi-admin/src/main/resources/mapper/system/InStockinDetailNFinishMapper.xml`
- `ruoyi-admin/src/main/java/com/ruoyi/system/service/IInStockinNFinishService.java`
- `ruoyi-admin/src/main/java/com/ruoyi/system/service/IInStockinDetailNFinishService.java`
- `ruoyi-admin/src/main/java/com/ruoyi/system/service/impl/InStockinNFinishServiceImpl.java`
- `ruoyi-admin/src/main/java/com/ruoyi/system/service/impl/InStockinDetailNFinishServiceImpl.java`
## 方法签名(示例)
- InStockinNFinishMapper / Service:
- `InStockinN selectByStockinGuid(String stockinGuid)`
- `List<InStockinN> selectList(InStockinN query)`
- `int insert(InStockinN row)` / `int update(InStockinN row)` / `int deleteByStockinGuid(String stockinGuid)`
- `int batchArchiveFromSource(String date)`(可选:将当日数据从源表复制到 finish 表)
- InStockinDetailNFinishMapper / Service:
- `InStockinDetailN selectByStockinDetailId(String stockinDetailId)`
- `List<InStockinDetailN> selectListByStockinGuid(String stockinGuid)`
- `int insert(InStockinDetailN row)` / `int update(InStockinDetailN row)` / `int deleteByStockinDetailId(String stockinDetailId)`
- `int batchArchiveByStockinGuid(String stockinGuid)`(从源明细表按主单 guid 复制到 finish 表)
## 归档流程(可在 Service 中提供便捷方法)
- 主单:`archiveStockinNByGuid(stockinGuid)`
- 从 `in_stockin_n` 查询主单,存在则插入/更新到 `in_stockin_n_finish`
- 子单:`archiveStockinDetailNByGuid(stockinGuid)`
- 从 `in_stockin_detail_n` 批量查询 `stockin_guid=...` 的子单,批量插入到 `in_stockin_detail_n_finish`
- 幂等:`insert` 使用 `stockin_guid` 或 `stockin_detail_id` 作为唯一约束进行“存在则更新”的 upsert 逻辑(与现有 XML 风格保持一致)。
## 校验与验证
- 本地运行现有入库拉取逻辑,手工触发一次归档方法,验证两张 finish 表插入成功。
- 使用已有前端页面查询与导出功能验证数据一致性(如果后续增加 Controller,将绑定至 `/system/inStockInFinish` 与 `/system/inStockInDetailFinish`)。
## 不改动项
- 不修改实体字段与现有源表 Mapper/Service;Finish 仅新增并依赖现有结构。
- 不自动生成 SQL DDL(如需要我可根据现有表结构快速给出 `_finish` 建表 SQL)。
## 实施说明
- 所有新增 Java/XML 文件将包含详细中文注释,解释与源表的字段一致性、使用场景与幂等处理原因。
- 完成后不进行 Git 提交,等待您提供提交说明/提交人/提交时间/提交备注后再执行。