3 小时以前 b22ebe8fad1691b35adcf321fe2e136795b3f81d
库位
已添加2个文件
已修改8个文件
228 ■■■■■ 文件已修改
docs/purchase_product_location.md 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/purchase_product_location.sql 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveBusinessStatusService.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceLedgerController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/purchase_product_location.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,151 @@
# é‡‡è´­å°è´¦åº“位字段(设备选择)
## æ¶‰åŠé¡µé¢
- é‡‡è´­å°è´¦ç®¡ç†é¡µé¢ (`views/procurementManagement/procurementLedger/index.vue`)
- é‡‡è´­å°è´¦æ–°å¢ž/编辑弹窗
- åº“存管理页面 (`views/inventoryManagement/stockManagement/index.vue`)
- å…¥åº“记录页面 (`views/inventoryManagement/stockManagement/Record.vue`)
## å­—段映射关系
| è¡¨/实体 | å­—段名 | è¯´æ˜Ž |
|---------|--------|------|
| device_ledger | storage_location | è®¾å¤‡çš„存放位置(库位) |
| sales_ledger_product | storage_location | é‡‡è´­å°è´¦äº§å“æ˜Žç»†çš„库位(来源于设备) |
| sales_ledger_product | device_id | å…³è”的设备ID |
| stock_in_record | warehouse | å…¥åº“记录的库位(来源于storageLocation) |
| stock_inventory | warehouse | åº“存的库位(来源于storageLocation) |
**数据流向:**
```
设备台账(storage_location) â†’ é‡‡è´­äº§å“æ˜Žç»†(storage_location) â†’ å…¥åº“记录(warehouse) â†’ åº“å­˜(warehouse)
```
## API
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /device/ledger/iotDeviceList | èŽ·å–IoT设备列表(is_iot_device=1) |
| POST | /purchase/purchaseLedger/addOrEditPurchase | æ–°å¢ž/编辑采购台账 |
| GET | /purchase/purchaseLedger/getPurchaseById | æŸ¥è¯¢é‡‡è´­å°è´¦è¯¦æƒ… |
| GET | /stock/stockInventory/pagestockInventory | åº“存分页查询 |
| GET | /stock/stockInventory/pageListCombinedStockInventory | åº“存合并查询 |
| GET | /stock/stockInRecord/listPage | å…¥åº“记录分页查询 |
**IoT设备列表响应:**
```json
[
  {
    "id": 1,
    "deviceName": "储罐A",
    "storageLocation": "仓库1-A区"
  }
]
```
**请求参数(产品明细新增字段):**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| deviceId | Long | å¦ | è®¾å¤‡ID(选择IoT设备后自动获取) |
| storageLocation | String | å¦ | åº“位(选择设备后自动填充设备的storageLocation) |
**响应字段(产品明细回显):**
| å‚æ•° | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| deviceId | Long | è®¾å¤‡ID |
| deviceName | String | è®¾å¤‡åç§° |
| storageLocation | String | åº“位 |
**响应字段(库存/入库记录回显):**
| å‚æ•° | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| warehouse | String | åº“位(来源于采购台账产品明细的storageLocation) |
## å‰ç«¯ä¿®æ”¹ç‚¹
### 1. é‡‡è´­å°è´¦äº§å“è¡¨æ ¼æ–°å¢žè®¾å¤‡é€‰æ‹©åˆ—
```html
<el-table-column label="选择设备" prop="deviceId" min-width="150">
  <template slot-scope="scope">
    <el-select
      v-model="scope.row.deviceId"
      placeholder="请选择设备"
      filterable
      @change="handleDeviceChange(scope.row, scope.$index)">
      <el-option
        v-for="device in iotDeviceList"
        :key="device.id"
        :label="device.deviceName"
        :value="device.id">
      </el-option>
    </el-select>
  </template>
</el-table-column>
<el-table-column label="库位" prop="storageLocation" min-width="120">
  <template slot-scope="scope">
    <el-input v-model="scope.row.storageLocation" placeholder="自动获取" disabled />
  </template>
</el-table-column>
```
### 2. åº“å­˜/入库记录表格新增库位列
```html
<el-table-column label="库位" prop="warehouse" min-width="120" />
```
### 3. data æ•°æ®
```js
data() {
  return {
    iotDeviceList: [], // IoT设备列表
    productData: [], // äº§å“æ˜Žç»†
  }
}
```
### 4. methods æ–¹æ³•
```js
// èŽ·å–IoT设备列表
async getIotDeviceList() {
  const res = await this.$axios.get('/device/ledger/iotDeviceList');
  this.iotDeviceList = res.data;
},
// è®¾å¤‡é€‰æ‹©å˜æ›´æ—¶ï¼Œè‡ªåŠ¨å¡«å……åº“ä½
handleDeviceChange(row, index) {
  const device = this.iotDeviceList.find(d => d.id === row.deviceId);
  if (device) {
    row.storageLocation = device.storageLocation;
  } else {
    row.storageLocation = '';
  }
},
// ç¼–辑回显时,根据deviceId回显设备名称
// æ³¨æ„ï¼šåŽç«¯æŸ¥è¯¢å·²å…³è”返回deviceName,无需额外处理
// é¡µé¢åŠ è½½æ—¶èŽ·å–è®¾å¤‡åˆ—è¡¨
mounted() {
  this.getIotDeviceList();
}
```
## æ³¨æ„äº‹é¡¹
- è®¾å¤‡é€‰æ‹©æŽ¥å£åªè¿”回 `is_iot_device=1` çš„设备(物联设备)
- é€‰æ‹©è®¾å¤‡åŽï¼Œåº“位自动从设备的 `storageLocation` å­—段获取,无需手动输入
- ç¼–辑采购台账时,后端会自动回显 `deviceId`、`deviceName`、`storageLocation` å­—段
- åº“位字段仅用于采购台账的原材料产品,销售台账的成品不需要此字段
- åº“位信息会在采购入库流程审批通过后自动带入到库存记录的 `warehouse` å­—段
- åº“存管理页面和入库记录页面的 `warehouse` å­—段已自动回显库位信息
- æ³¨æ„å­—段命名差异:采购台账用 `storageLocation`,库存用 `warehouse`,实际存储的是同一个库位值
docs/purchase_product_location.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
-- é‡‡è´­å°è´¦äº§å“æ·»åŠ åº“ä½å­—æ®µï¼ˆè®¾å¤‡é€‰æ‹©ï¼‰
-- æ—¥æœŸ: 2026-06-29
-- è¯´æ˜Ž: åŽŸææ–™é‡‡è´­å…¥åº“éœ€è¦ä»ŽIoT设备台账选择库位,通过流程带入到库存记录
-- ä¸ºé‡‡è´­å°è´¦äº§å“è¡¨æ·»åŠ è®¾å¤‡ID和库位字段
ALTER TABLE sales_ledger_product ADD COLUMN device_id BIGINT DEFAULT NULL COMMENT '设备ID(关联设备台账,筛选is_iot_device=1)';
ALTER TABLE sales_ledger_product ADD COLUMN storage_location VARCHAR(100) DEFAULT NULL COMMENT '库位(来源于设备的storage_location)';
-- ä¸ºè´¨æ£€å•表添加库位字段(采购质检入库时使用)
ALTER TABLE quality_inspect ADD COLUMN warehouse VARCHAR(100) DEFAULT NULL COMMENT '库位(来源于采购产品明细的storage_location)';
-- æ³¨æ„äº‹é¡¹:
-- 1. æ­¤å­—段仅用于采购台账(type=2)的原材料产品
-- 2. é”€å”®å°è´¦çš„æˆå“åº“存不需要此字段
-- 3. å‰ç«¯é€‰æ‹©è®¾å¤‡æ—¶ï¼Œéœ€è°ƒç”¨ GET /device/ledger/iotDeviceList æŽ¥å£èŽ·å– is_iot_device=1 çš„设备列表
-- 4. é€‰æ‹©è®¾å¤‡åŽï¼Œè‡ªåŠ¨èŽ·å–è®¾å¤‡çš„ storage_location å­—段值
-- 5. åº“位信息流转路径:
--    è®¾å¤‡(storage_location) â†’ é‡‡è´­äº§å“æ˜Žç»†(storage_location) â†’ è´¨æ£€å•(warehouse) â†’ å…¥åº“记录(warehouse) â†’ åº“å­˜(warehouse)
--    æˆ–:设备(storage_location) â†’ é‡‡è´­äº§å“æ˜Žç»†(storage_location) â†’ å…¥åº“记录(warehouse) â†’ åº“å­˜(warehouse)(直接入库)
src/main/java/com/ruoyi/approve/service/impl/ApproveBusinessStatusService.java
@@ -87,7 +87,9 @@
                            salesLedgerProduct.getQuantity(),
                            StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
                            purchaseLedger.getId(),
                            purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId());
                            purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId(),
                            null,
                            salesLedgerProduct.getStorageLocation());
                }
            }
        } else if (status.equals(3)) {
@@ -156,6 +158,7 @@
        qualityInspect.setProductModelId(saleProduct.getProductModelId());
        qualityInspect.setUnit(saleProduct.getUnit());
        qualityInspect.setQuantity(saleProduct.getQuantity());
        qualityInspect.setWarehouse(saleProduct.getStorageLocation());
        qualityInspectMapper.insert(qualityInspect);
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null);
        if (qualityTestStandard.size() > 0) {
src/main/java/com/ruoyi/device/controller/DeviceLedgerController.java
@@ -113,4 +113,13 @@
        deviceLedger.setCreateTime(deviceLedger.getUpdateTime().plusMonths(1));//下次维护时间
        return AjaxResult.success(deviceLedger);
    }
    @GetMapping("iotDeviceList")
    @Operation(summary = "获取IoT设备列表(is_iot_device=1)")
    public AjaxResult iotDeviceList() {
        return AjaxResult.success(deviceLedgerMapper.selectList(
                new QueryWrapper<DeviceLedger>().lambda()
                        .eq(DeviceLedger::getIsIotDevice, 1)
                        .select(DeviceLedger::getId, DeviceLedger::getDeviceName, DeviceLedger::getStorageLocation)));
    }
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -64,6 +64,8 @@
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.device.mapper.DeviceLedgerMapper;
import com.ruoyi.device.pojo.DeviceLedger;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -119,6 +121,7 @@
    private final StockUtils stockUtils;
    private final ApprovalTemplateMapper approvalTemplateMapper;
    private final SparePartsMapper sparePartsMapper;
    private final DeviceLedgerMapper deviceLedgerMapper;
    @Override
    public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
@@ -421,6 +424,7 @@
        qualityInspect.setProductModelId(saleProduct.getProductModelId());
        qualityInspect.setUnit(saleProduct.getUnit());
        qualityInspect.setQuantity(saleProduct.getQuantity());
        qualityInspect.setWarehouse(saleProduct.getStorageLocation());
        qualityInspectMapper.insert(qualityInspect);
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0,null);
        if (qualityTestStandard.size()>0){
@@ -495,13 +499,18 @@
        if (CollectionUtils.isEmpty(stockRecords)
                && qualityInspect.getQualifiedQuantity() != null
                && qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
            // ä¼˜å…ˆä½¿ç”¨è´¨æ£€å•中的库位,如果没有则使用产品明细中的库位
            String warehouse = StringUtils.hasText(qualityInspect.getWarehouse())
                    ? qualityInspect.getWarehouse()
                    : product.getStorageLocation();
            stockUtils.addStockWithBatchNo(
                    product.getProductModelId(),
                    qualityInspect.getQualifiedQuantity(),
                    StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode(),
                    qualityInspect.getId(),
                    null,
                    purchaseInspectTime == null ? null : purchaseInspectTime.plusDays(1)
                    purchaseInspectTime == null ? null : purchaseInspectTime.plusDays(1),
                    warehouse
            );
            stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
        }
@@ -548,7 +557,8 @@
                    StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
                    purchaseLedger.getId(),
                    purchaseLedger.getPurchaseContractNumber() + "-" + product.getId(),
                    stockCreateTime
                    stockCreateTime,
                    product.getStorageLocation()
            );
            stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
        }
@@ -735,6 +745,14 @@
                    product.setProductCategory(spareParts.getName());
                }
            }
            // å¦‚果有设备ID但没有库位,从设备表自动填充库位
            if (product.getDeviceId() != null && StringUtils.isEmpty(product.getStorageLocation())) {
                DeviceLedger device = deviceLedgerMapper.selectById(product.getDeviceId());
                if (device != null && StringUtils.isNotEmpty(device.getStorageLocation())) {
                    product.setStorageLocation(device.getStorageLocation());
                }
            }
        }
        // åˆ†ç»„处理
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -191,6 +191,9 @@
    @Excel(name = "自动判断结果")
    private String autoJudgeResult;
    @Schema(description = "库位(来源于采购产品明细)")
    private String warehouse;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -188,4 +188,14 @@
    @TableField(exist = false)
    @Schema(description = "待发货数量")
    private BigDecimal noQuantity;
    @Schema(description = "设备ID(关联设备台账,筛选is_iot_device=1)")
    private Long deviceId;
    @TableField(exist = false)
    @Schema(description = "设备名称")
    private String deviceName;
    @Schema(description = "库位(来源于设备的storage_location)")
    private String storageLocation;
}
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -242,6 +242,7 @@
                    stockInventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockInventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryDto.setRemark(stockInRecord.getRemark());
                    stockInventoryDto.setWarehouse(stockInRecord.getWarehouse());
                    if (stockInventory == null) {
                        stockInventoryMapper.insert(new StockInventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
@@ -263,6 +264,7 @@
                    stockUninventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockUninventoryDto.setRemark(stockInRecord.getRemark());
                    stockUninventoryDto.setWarehouse(stockInRecord.getWarehouse());
                    if (stockUninventory == null) {
                        stockUninventoryMapper.insert(new StockUninventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -26,9 +26,12 @@
        T1.is_production,
        T1.create_user,
        T1.dept_id,
        T1.device_id,
        T1.storage_location,
        p.product_name as product_category,
        pm.model as specification_model,
        pm.unit as unit,
        dl.device_name,
        CASE
        WHEN (IFNULL(t2.qualitity, 0) - IFNULL(t2.locked_quantity, 0)) >0 THEN 1
        ELSE 0
@@ -101,6 +104,7 @@
        ) t4 ON t4.sales_ledger_product_id = T1.id
        left join product_model pm ON T1.product_model_id = pm.id
        left join product p ON pm.product_id = p.id
        left join device_ledger dl ON T1.device_id = dl.id
        <where>
            <if test="salesLedgerProduct.salesLedgerId != null">
                AND T1.sales_ledger_id = #{salesLedgerProduct.salesLedgerId}
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -231,7 +231,8 @@
        model,
        unit,
        product_name,
        product_id
        product_id,
        warehouse
        order by create_time desc
    </select>