feat(asset): 新增资产库存明细功能
- 创建 AmsAssetStock 实体类用于库存汇总统计 - 实现 AmsAssetStockController 提供库存查询和导出功能 - 开发 AmsAssetStockMapper 和 XML 映射文件进行数据库操作 - 添加 AmsAssetStockService 业务逻辑处理层 - 集成前端 stock.html 页面实现库存汇总和明细查看 - 支持按分类、名称、仓库、状态等条件筛选库存 - 实现点击汇总行查看对应资产明细的功能 - 添加单元测试验证库存查询逻辑的正确性main
parent
d587e1a8b1
commit
5fae393ac0
@ -0,0 +1,117 @@
|
||||
package com.ruoyi.asset.controller;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import com.ruoyi.asset.constant.AssetStatus;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetCategory;
|
||||
import com.ruoyi.asset.domain.AmsAssetStock;
|
||||
import com.ruoyi.asset.domain.AmsWarehouse;
|
||||
import com.ruoyi.asset.service.IAmsAssetCategoryService;
|
||||
import com.ruoyi.asset.service.IAmsAssetStockService;
|
||||
import com.ruoyi.asset.service.IAmsWarehouseService;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
|
||||
/**
|
||||
* 资产库存明细Controller
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/asset/stock")
|
||||
public class AmsAssetStockController extends BaseController
|
||||
{
|
||||
private static final String ENABLED_YES = "Y";
|
||||
|
||||
private String prefix = "asset/stock";
|
||||
|
||||
@Autowired
|
||||
private IAmsAssetStockService amsAssetStockService;
|
||||
|
||||
@Autowired
|
||||
private IAmsAssetCategoryService amsAssetCategoryService;
|
||||
|
||||
@Autowired
|
||||
private IAmsWarehouseService amsWarehouseService;
|
||||
|
||||
@RequiresPermissions("asset:stock:view")
|
||||
@GetMapping()
|
||||
public String stock(ModelMap mmap)
|
||||
{
|
||||
putStockOptions(mmap);
|
||||
mmap.put("defaultAssetStatus", AssetStatus.IN_STOCK);
|
||||
return prefix + "/stock";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询资产库存汇总列表。
|
||||
*/
|
||||
@RequiresPermissions("asset:stock:list")
|
||||
@PostMapping("/list")
|
||||
@ResponseBody
|
||||
public TableDataInfo list(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
startPage();
|
||||
List<AmsAssetStock> list = amsAssetStockService.selectAmsAssetStockList(amsAssetStock);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询库存汇总行下的一物一码资产明细。
|
||||
*/
|
||||
@RequiresPermissions("asset:stock:detail")
|
||||
@PostMapping("/detail")
|
||||
@ResponseBody
|
||||
public TableDataInfo detail(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
startPage();
|
||||
List<AmsAsset> list = amsAssetStockService.selectAmsAssetStockDetailList(amsAssetStock);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出资产库存汇总列表。
|
||||
*/
|
||||
@RequiresPermissions("asset:stock:export")
|
||||
@Log(title = "资产库存明细", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@ResponseBody
|
||||
public AjaxResult export(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
List<AmsAssetStock> list = amsAssetStockService.selectAmsAssetStockList(amsAssetStock);
|
||||
ExcelUtil<AmsAssetStock> util = new ExcelUtil<AmsAssetStock>(AmsAssetStock.class);
|
||||
return util.exportExcel(list, "资产库存明细数据");
|
||||
}
|
||||
|
||||
private void putStockOptions(ModelMap mmap)
|
||||
{
|
||||
mmap.put("categoryList", selectEnabledCategoryList());
|
||||
mmap.put("warehouseList", selectEnabledWarehouseList());
|
||||
}
|
||||
|
||||
private List<AmsAssetCategory> selectEnabledCategoryList()
|
||||
{
|
||||
AmsAssetCategory category = new AmsAssetCategory();
|
||||
category.setEnabled(ENABLED_YES);
|
||||
return amsAssetCategoryService.selectAmsAssetCategoryList(category);
|
||||
}
|
||||
|
||||
private List<AmsWarehouse> selectEnabledWarehouseList()
|
||||
{
|
||||
AmsWarehouse warehouse = new AmsWarehouse();
|
||||
warehouse.setEnabled(ENABLED_YES);
|
||||
return amsWarehouseService.selectAmsWarehouseList(warehouse);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.ruoyi.asset.domain;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.annotation.Excel.ColumnType;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 资产库存明细汇总对象。
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
public class AmsAssetStock extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 内部汇总键,用于下钻明细时精确匹配空值维度 */
|
||||
private String groupKey;
|
||||
|
||||
/** 资产类别ID */
|
||||
private Long categoryId;
|
||||
|
||||
/** 类别编码 */
|
||||
private String categoryCode;
|
||||
|
||||
/** 类别名称 */
|
||||
@Excel(name = "资产类别")
|
||||
private String categoryName;
|
||||
|
||||
/** 资产名称 */
|
||||
@Excel(name = "资产名称")
|
||||
private String assetName;
|
||||
|
||||
/** 规格型号 */
|
||||
@Excel(name = "规格型号")
|
||||
private String specModel;
|
||||
|
||||
/** 品牌 */
|
||||
@Excel(name = "品牌")
|
||||
private String brand;
|
||||
|
||||
/** 仓库ID */
|
||||
private Long warehouseId;
|
||||
|
||||
/** 仓库编码 */
|
||||
private String warehouseCode;
|
||||
|
||||
/** 仓库名称 */
|
||||
@Excel(name = "所属仓库")
|
||||
private String warehouseName;
|
||||
|
||||
/** 位置ID */
|
||||
private Long locationId;
|
||||
|
||||
/** 位置编码 */
|
||||
private String locationCode;
|
||||
|
||||
/** 位置名称 */
|
||||
private String locationName;
|
||||
|
||||
/** 资产状态 */
|
||||
@Excel(name = "资产状态", dictType = "ams_asset_status")
|
||||
private String assetStatus;
|
||||
|
||||
/** 库存数量 */
|
||||
@Excel(name = "库存数量", cellType = ColumnType.NUMERIC)
|
||||
private Long stockQuantity;
|
||||
|
||||
public String getGroupKey()
|
||||
{
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey)
|
||||
{
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public Long getCategoryId()
|
||||
{
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(Long categoryId)
|
||||
{
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public String getCategoryCode()
|
||||
{
|
||||
return categoryCode;
|
||||
}
|
||||
|
||||
public void setCategoryCode(String categoryCode)
|
||||
{
|
||||
this.categoryCode = categoryCode;
|
||||
}
|
||||
|
||||
public String getCategoryName()
|
||||
{
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void setCategoryName(String categoryName)
|
||||
{
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public String getAssetName()
|
||||
{
|
||||
return assetName;
|
||||
}
|
||||
|
||||
public void setAssetName(String assetName)
|
||||
{
|
||||
this.assetName = assetName;
|
||||
}
|
||||
|
||||
public String getSpecModel()
|
||||
{
|
||||
return specModel;
|
||||
}
|
||||
|
||||
public void setSpecModel(String specModel)
|
||||
{
|
||||
this.specModel = specModel;
|
||||
}
|
||||
|
||||
public String getBrand()
|
||||
{
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand)
|
||||
{
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public Long getWarehouseId()
|
||||
{
|
||||
return warehouseId;
|
||||
}
|
||||
|
||||
public void setWarehouseId(Long warehouseId)
|
||||
{
|
||||
this.warehouseId = warehouseId;
|
||||
}
|
||||
|
||||
public String getWarehouseCode()
|
||||
{
|
||||
return warehouseCode;
|
||||
}
|
||||
|
||||
public void setWarehouseCode(String warehouseCode)
|
||||
{
|
||||
this.warehouseCode = warehouseCode;
|
||||
}
|
||||
|
||||
public String getWarehouseName()
|
||||
{
|
||||
return warehouseName;
|
||||
}
|
||||
|
||||
public void setWarehouseName(String warehouseName)
|
||||
{
|
||||
this.warehouseName = warehouseName;
|
||||
}
|
||||
|
||||
public Long getLocationId()
|
||||
{
|
||||
return locationId;
|
||||
}
|
||||
|
||||
public void setLocationId(Long locationId)
|
||||
{
|
||||
this.locationId = locationId;
|
||||
}
|
||||
|
||||
public String getLocationCode()
|
||||
{
|
||||
return locationCode;
|
||||
}
|
||||
|
||||
public void setLocationCode(String locationCode)
|
||||
{
|
||||
this.locationCode = locationCode;
|
||||
}
|
||||
|
||||
public String getLocationName()
|
||||
{
|
||||
return locationName;
|
||||
}
|
||||
|
||||
public void setLocationName(String locationName)
|
||||
{
|
||||
this.locationName = locationName;
|
||||
}
|
||||
|
||||
public String getAssetStatus()
|
||||
{
|
||||
return assetStatus;
|
||||
}
|
||||
|
||||
public void setAssetStatus(String assetStatus)
|
||||
{
|
||||
this.assetStatus = assetStatus;
|
||||
}
|
||||
|
||||
public Long getStockQuantity()
|
||||
{
|
||||
return stockQuantity;
|
||||
}
|
||||
|
||||
public void setStockQuantity(Long stockQuantity)
|
||||
{
|
||||
this.stockQuantity = stockQuantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("groupKey", getGroupKey())
|
||||
.append("categoryId", getCategoryId())
|
||||
.append("categoryCode", getCategoryCode())
|
||||
.append("categoryName", getCategoryName())
|
||||
.append("assetName", getAssetName())
|
||||
.append("specModel", getSpecModel())
|
||||
.append("brand", getBrand())
|
||||
.append("warehouseId", getWarehouseId())
|
||||
.append("warehouseCode", getWarehouseCode())
|
||||
.append("warehouseName", getWarehouseName())
|
||||
.append("locationId", getLocationId())
|
||||
.append("locationCode", getLocationCode())
|
||||
.append("locationName", getLocationName())
|
||||
.append("assetStatus", getAssetStatus())
|
||||
.append("stockQuantity", getStockQuantity())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.ruoyi.asset.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetStock;
|
||||
|
||||
/**
|
||||
* 资产库存明细Mapper接口
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
public interface AmsAssetStockMapper
|
||||
{
|
||||
/**
|
||||
* 查询资产库存汇总列表。
|
||||
*
|
||||
* @param amsAssetStock 查询条件
|
||||
* @return 库存汇总集合
|
||||
*/
|
||||
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock);
|
||||
|
||||
/**
|
||||
* 查询库存汇总行对应的一物一码资产明细。
|
||||
*
|
||||
* @param amsAssetStock 查询条件
|
||||
* @return 资产明细集合
|
||||
*/
|
||||
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.ruoyi.asset.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetStock;
|
||||
|
||||
/**
|
||||
* 资产库存明细Service接口
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
public interface IAmsAssetStockService
|
||||
{
|
||||
/**
|
||||
* 查询资产库存汇总列表。
|
||||
*
|
||||
* @param amsAssetStock 查询条件
|
||||
* @return 库存汇总集合
|
||||
*/
|
||||
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock);
|
||||
|
||||
/**
|
||||
* 查询库存汇总行对应的一物一码资产明细。
|
||||
*
|
||||
* @param amsAssetStock 查询条件
|
||||
* @return 资产明细集合
|
||||
*/
|
||||
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock);
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.ruoyi.asset.service.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.asset.constant.AssetStatus;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetStock;
|
||||
import com.ruoyi.asset.mapper.AmsAssetStockMapper;
|
||||
import com.ruoyi.asset.service.IAmsAssetStockService;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 资产库存明细Service业务层处理
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
@Service
|
||||
public class AmsAssetStockServiceImpl implements IAmsAssetStockService
|
||||
{
|
||||
@Autowired
|
||||
private AmsAssetStockMapper amsAssetStockMapper;
|
||||
|
||||
/**
|
||||
* 查询资产库存汇总列表。
|
||||
*/
|
||||
@Override
|
||||
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
AmsAssetStock query = defaultStockQuery(amsAssetStock);
|
||||
return amsAssetStockMapper.selectAmsAssetStockList(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询库存汇总行对应的一物一码资产明细。
|
||||
*/
|
||||
@Override
|
||||
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
AmsAssetStock query = defaultStockQuery(amsAssetStock);
|
||||
if (StringUtils.isEmpty(query.getGroupKey()))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return amsAssetStockMapper.selectAmsAssetStockDetailList(query);
|
||||
}
|
||||
|
||||
private AmsAssetStock defaultStockQuery(AmsAssetStock amsAssetStock)
|
||||
{
|
||||
AmsAssetStock query = amsAssetStock == null ? new AmsAssetStock() : amsAssetStock;
|
||||
if (query.getAssetStatus() == null)
|
||||
{
|
||||
query.setAssetStatus(AssetStatus.IN_STOCK);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.asset.mapper.AmsAssetStockMapper">
|
||||
|
||||
<resultMap type="AmsAssetStock" id="AmsAssetStockResult">
|
||||
<result property="groupKey" column="group_key" />
|
||||
<result property="categoryId" column="category_id" />
|
||||
<result property="categoryCode" column="category_code" />
|
||||
<result property="categoryName" column="category_name" />
|
||||
<result property="assetName" column="asset_name" />
|
||||
<result property="specModel" column="spec_model" />
|
||||
<result property="brand" column="brand" />
|
||||
<result property="warehouseId" column="warehouse_id" />
|
||||
<result property="warehouseCode" column="warehouse_code" />
|
||||
<result property="warehouseName" column="warehouse_name" />
|
||||
<result property="assetStatus" column="asset_status" />
|
||||
<result property="stockQuantity" column="stock_quantity" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="AmsAsset" id="AmsAssetStockDetailResult">
|
||||
<result property="assetId" column="asset_id" />
|
||||
<result property="assetCode" column="asset_code" />
|
||||
<result property="assetName" column="asset_name" />
|
||||
<result property="categoryId" column="category_id" />
|
||||
<result property="categoryCode" column="category_code" />
|
||||
<result property="categoryName" column="category_name" />
|
||||
<result property="specModel" column="spec_model" />
|
||||
<result property="brand" column="brand" />
|
||||
<result property="warehouseId" column="warehouse_id" />
|
||||
<result property="warehouseCode" column="warehouse_code" />
|
||||
<result property="warehouseName" column="warehouse_name" />
|
||||
<result property="locationId" column="location_id" />
|
||||
<result property="locationCode" column="location_code" />
|
||||
<result property="locationName" column="location_name" />
|
||||
<result property="useDeptName" column="use_dept_name" />
|
||||
<result property="useUserName" column="use_user_name" />
|
||||
<result property="assetStatus" column="asset_status" />
|
||||
<result property="tagCode" column="tag_code" />
|
||||
<result property="epcCode" column="epc_code" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="assetStockGroupKey">
|
||||
sha2(concat_ws('|',
|
||||
ifnull(cast(category_id as char), '__NULL__'),
|
||||
ifnull(category_code, '__NULL__'),
|
||||
ifnull(category_name, '__NULL__'),
|
||||
ifnull(asset_name, '__NULL__'),
|
||||
ifnull(spec_model, '__NULL__'),
|
||||
ifnull(brand, '__NULL__'),
|
||||
ifnull(cast(warehouse_id as char), '__NULL__'),
|
||||
ifnull(warehouse_code, '__NULL__'),
|
||||
ifnull(warehouse_name, '__NULL__'),
|
||||
ifnull(asset_status, '__NULL__')
|
||||
), 256)
|
||||
</sql>
|
||||
|
||||
<sql id="assetStockWhere">
|
||||
<where>
|
||||
del_flag = '0'
|
||||
<if test="categoryId != null"> and category_id = #{categoryId}</if>
|
||||
<if test="assetName != null and assetName != ''"> and asset_name like concat('%', #{assetName}, '%')</if>
|
||||
<if test="specModel != null and specModel != ''"> and spec_model like concat('%', #{specModel}, '%')</if>
|
||||
<if test="brand != null and brand != ''"> and brand like concat('%', #{brand}, '%')</if>
|
||||
<if test="warehouseId != null"> and warehouse_id = #{warehouseId}</if>
|
||||
<if test="assetStatus != null and assetStatus != ''"> and asset_status = #{assetStatus}</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<select id="selectAmsAssetStockList" parameterType="AmsAssetStock" resultMap="AmsAssetStockResult">
|
||||
select
|
||||
<include refid="assetStockGroupKey"/> as group_key,
|
||||
category_id,
|
||||
category_code,
|
||||
category_name,
|
||||
asset_name,
|
||||
spec_model,
|
||||
brand,
|
||||
warehouse_id,
|
||||
warehouse_code,
|
||||
warehouse_name,
|
||||
asset_status,
|
||||
count(1) as stock_quantity
|
||||
from ams_asset
|
||||
<include refid="assetStockWhere"/>
|
||||
group by
|
||||
category_id,
|
||||
category_code,
|
||||
category_name,
|
||||
asset_name,
|
||||
spec_model,
|
||||
brand,
|
||||
warehouse_id,
|
||||
warehouse_code,
|
||||
warehouse_name,
|
||||
asset_status
|
||||
order by category_name, asset_name, spec_model, brand, warehouse_name, asset_status
|
||||
</select>
|
||||
|
||||
<select id="selectAmsAssetStockDetailList" parameterType="AmsAssetStock" resultMap="AmsAssetStockDetailResult">
|
||||
select asset_id,
|
||||
asset_code,
|
||||
asset_name,
|
||||
category_id,
|
||||
category_code,
|
||||
category_name,
|
||||
spec_model,
|
||||
brand,
|
||||
warehouse_id,
|
||||
warehouse_code,
|
||||
warehouse_name,
|
||||
location_id,
|
||||
location_code,
|
||||
location_name,
|
||||
use_dept_name,
|
||||
use_user_name,
|
||||
asset_status,
|
||||
tag_code,
|
||||
epc_code
|
||||
from ams_asset
|
||||
where del_flag = '0'
|
||||
and asset_status = #{assetStatus}
|
||||
and <include refid="assetStockGroupKey"/> = #{groupKey}
|
||||
order by asset_code
|
||||
</select>
|
||||
</mapper>
|
||||
@ -0,0 +1,230 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
|
||||
<head>
|
||||
<th:block th:include="include :: header('资产库存明细')" />
|
||||
<th:block th:include="include :: select2-css" />
|
||||
</head>
|
||||
<body class="gray-bg">
|
||||
<div class="container-div">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 search-collapse">
|
||||
<form id="formId">
|
||||
<div class="select-list">
|
||||
<ul>
|
||||
<li>
|
||||
<label>资产类别:</label>
|
||||
<select name="categoryId" class="select2-control" style="width: 170px">
|
||||
<option value="">所有</option>
|
||||
<option th:each="category : ${categoryList}" th:text="${category.categoryName}" th:value="${category.categoryId}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label>资产名称:</label>
|
||||
<input type="text" name="assetName"/>
|
||||
</li>
|
||||
<li>
|
||||
<label>规格型号:</label>
|
||||
<input type="text" name="specModel"/>
|
||||
</li>
|
||||
<li>
|
||||
<label>品牌:</label>
|
||||
<input type="text" name="brand"/>
|
||||
</li>
|
||||
<li>
|
||||
<label>所属仓库:</label>
|
||||
<select name="warehouseId" class="select2-control" style="width: 170px">
|
||||
<option value="">所有</option>
|
||||
<option th:each="warehouse : ${warehouseList}" th:text="${warehouse.warehouseName}" th:value="${warehouse.warehouseId}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label>资产状态:</label>
|
||||
<select name="assetStatus" th:with="type=${@dict.getType('ams_asset_status')}">
|
||||
<option value="">所有</option>
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:selected="${dict.dictValue == defaultAssetStatus}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<a class="btn btn-primary btn-rounded btn-sm" onclick="searchStock()"><i class="fa fa-search"></i> 搜索</a>
|
||||
<a class="btn btn-warning btn-rounded btn-sm" onclick="resetSearch()"><i class="fa fa-refresh"></i> 重置</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="btn-group-sm" id="toolbar" role="group">
|
||||
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="asset:stock:export">
|
||||
<i class="fa fa-download"></i> 导出汇总
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="stock-table"></table>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<h4 class="form-header h4">库存构成明细</h4>
|
||||
</div>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="detail-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: select2-js" />
|
||||
<script th:inline="javascript">
|
||||
var detailFlag = [[${@permission.hasPermi('asset:stock:detail')}]];
|
||||
var assetStatusDatas = [[${@dict.getType('ams_asset_status')}]];
|
||||
var defaultAssetStatus = [[${defaultAssetStatus}]];
|
||||
var prefix = ctx + "asset/stock";
|
||||
var currentStockRow = null;
|
||||
|
||||
$(function() {
|
||||
$(".select2-control").select2({
|
||||
placeholder: "请选择",
|
||||
allowClear: true
|
||||
});
|
||||
initDetailTable();
|
||||
initStockTable();
|
||||
});
|
||||
|
||||
function initDetailTable() {
|
||||
var detailOptions = {
|
||||
id: "detail-table",
|
||||
toolbar: "detail-toolbar",
|
||||
url: prefix + "/detail",
|
||||
firstLoad: false,
|
||||
modalName: "库存构成明细",
|
||||
showSearch: false,
|
||||
showRefresh: false,
|
||||
showColumns: false,
|
||||
showToggle: false,
|
||||
queryParams: function(params) {
|
||||
params = $.extend(params, $.common.formToJSON("formId"));
|
||||
params.groupKey = currentStockRow ? currentStockRow.groupKey : "";
|
||||
params.assetStatus = currentStockRow ? currentStockRow.assetStatus : defaultAssetStatus;
|
||||
return params;
|
||||
},
|
||||
columns: [{
|
||||
field: 'assetCode',
|
||||
title: '资产编码'
|
||||
},
|
||||
{
|
||||
field: 'assetName',
|
||||
title: '资产名称'
|
||||
},
|
||||
{
|
||||
field: 'categoryName',
|
||||
title: '资产类别'
|
||||
},
|
||||
{
|
||||
field: 'specModel',
|
||||
title: '规格型号'
|
||||
},
|
||||
{
|
||||
field: 'brand',
|
||||
title: '品牌'
|
||||
},
|
||||
{
|
||||
field: 'warehouseName',
|
||||
title: '所属仓库'
|
||||
},
|
||||
{
|
||||
field: 'locationName',
|
||||
title: '存放位置'
|
||||
},
|
||||
{
|
||||
field: 'assetStatus',
|
||||
title: '资产状态',
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.selectDictLabel(assetStatusDatas, value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'tagCode',
|
||||
title: '标签编码'
|
||||
},
|
||||
{
|
||||
field: 'epcCode',
|
||||
title: 'EPC编码'
|
||||
}]
|
||||
};
|
||||
$.table.init(detailOptions);
|
||||
}
|
||||
|
||||
function initStockTable() {
|
||||
var options = {
|
||||
id: "stock-table",
|
||||
url: prefix + "/list",
|
||||
exportUrl: prefix + "/export",
|
||||
modalName: "资产库存明细",
|
||||
columns: [{
|
||||
field: 'groupKey',
|
||||
title: '汇总键',
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: 'categoryName',
|
||||
title: '资产类别'
|
||||
},
|
||||
{
|
||||
field: 'assetName',
|
||||
title: '资产名称'
|
||||
},
|
||||
{
|
||||
field: 'specModel',
|
||||
title: '规格型号'
|
||||
},
|
||||
{
|
||||
field: 'brand',
|
||||
title: '品牌'
|
||||
},
|
||||
{
|
||||
field: 'warehouseName',
|
||||
title: '所属仓库'
|
||||
},
|
||||
{
|
||||
field: 'assetStatus',
|
||||
title: '资产状态',
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.selectDictLabel(assetStatusDatas, value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'stockQuantity',
|
||||
title: '库存数量',
|
||||
align: 'right'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
formatter: function(value, row, index) {
|
||||
var actions = [];
|
||||
actions.push('<a class="btn btn-info btn-xs ' + detailFlag + '" href="javascript:void(0)" onclick="showStockDetail(' + index + ')"><i class="fa fa-list"></i>查看明细</a>');
|
||||
return actions.join('');
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
}
|
||||
|
||||
function showStockDetail(index) {
|
||||
currentStockRow = $("#stock-table").bootstrapTable("getData")[index];
|
||||
$("#detail-table").bootstrapTable("refresh", {pageNumber: 1});
|
||||
}
|
||||
|
||||
function searchStock() {
|
||||
currentStockRow = null;
|
||||
$("#detail-table").bootstrapTable("removeAll");
|
||||
$.table.search("formId", "stock-table");
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
$.form.reset();
|
||||
$(".select2-control").val("").trigger("change");
|
||||
$("select[name='assetStatus']").val(defaultAssetStatus).trigger("change");
|
||||
searchStock();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,101 @@
|
||||
package com.ruoyi.asset.service.impl;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.constant.AssetStatus;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetStock;
|
||||
import com.ruoyi.asset.mapper.AmsAssetStockMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AmsAssetStockServiceImplTest
|
||||
{
|
||||
@Mock
|
||||
private AmsAssetStockMapper amsAssetStockMapper;
|
||||
|
||||
@InjectMocks
|
||||
private AmsAssetStockServiceImpl service;
|
||||
|
||||
/** 库存页默认看在库资产,避免空状态查询把待入库、报废等非库存数据混入汇总 */
|
||||
@Test
|
||||
void selectAmsAssetStockListShouldDefaultToInStock()
|
||||
{
|
||||
AmsAssetStock query = new AmsAssetStock();
|
||||
when(amsAssetStockMapper.selectAmsAssetStockList(query)).thenReturn(List.of());
|
||||
|
||||
service.selectAmsAssetStockList(query);
|
||||
|
||||
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
|
||||
verify(amsAssetStockMapper).selectAmsAssetStockList(captor.capture());
|
||||
assertEquals(AssetStatus.IN_STOCK, captor.getValue().getAssetStatus());
|
||||
}
|
||||
|
||||
/** 用户显式选择状态时必须尊重筛选条件,支持查看维修中、借用中等资产分布 */
|
||||
@Test
|
||||
void selectAmsAssetStockListShouldRespectExplicitStatus()
|
||||
{
|
||||
AmsAssetStock query = new AmsAssetStock();
|
||||
query.setAssetStatus(AssetStatus.REPAIRING);
|
||||
when(amsAssetStockMapper.selectAmsAssetStockList(query)).thenReturn(List.of());
|
||||
|
||||
service.selectAmsAssetStockList(query);
|
||||
|
||||
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
|
||||
verify(amsAssetStockMapper).selectAmsAssetStockList(captor.capture());
|
||||
assertEquals(AssetStatus.REPAIRING, captor.getValue().getAssetStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectAmsAssetStockListShouldReturnStockQuantityFromMapper()
|
||||
{
|
||||
AmsAssetStock row = new AmsAssetStock();
|
||||
row.setAssetName("手持终端");
|
||||
row.setSpecModel("PDA-X1");
|
||||
row.setStockQuantity(3L);
|
||||
when(amsAssetStockMapper.selectAmsAssetStockList(any(AmsAssetStock.class))).thenReturn(List.of(row));
|
||||
|
||||
List<AmsAssetStock> result = service.selectAmsAssetStockList(new AmsAssetStock());
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(3L, result.get(0).getStockQuantity());
|
||||
}
|
||||
|
||||
/** 没有汇总键时不允许直接查明细,防止空条件误扫整张资产表 */
|
||||
@Test
|
||||
void selectAmsAssetStockDetailListShouldReturnEmptyWhenGroupKeyBlank()
|
||||
{
|
||||
AmsAssetStock query = new AmsAssetStock();
|
||||
|
||||
List<AmsAsset> result = service.selectAmsAssetStockDetailList(query);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(amsAssetStockMapper, never()).selectAmsAssetStockDetailList(any(AmsAssetStock.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectAmsAssetStockDetailListShouldUseGroupKeyAndDefaultStatus()
|
||||
{
|
||||
AmsAssetStock query = new AmsAssetStock();
|
||||
query.setGroupKey("group-1");
|
||||
when(amsAssetStockMapper.selectAmsAssetStockDetailList(query)).thenReturn(List.of(new AmsAsset()));
|
||||
|
||||
service.selectAmsAssetStockDetailList(query);
|
||||
|
||||
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
|
||||
verify(amsAssetStockMapper).selectAmsAssetStockDetailList(captor.capture());
|
||||
assertEquals("group-1", captor.getValue().getGroupKey());
|
||||
assertEquals(AssetStatus.IN_STOCK, captor.getValue().getAssetStatus());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue