feat(asset): 添加资产入库管理功能
- 新增入库单页面实现资产入库流程 - 添加入库订单实体类及明细项目实体类 - 实现入库管理控制器提供完整的CRUD操作 - 创建入库订单数据访问层及映射文件 - 开发入库服务业务逻辑处理 - 集成仓库、资产位置和资产数据联动功能 - 实现入库单状态管理和确认入库功能 - 添加入库单导出和权限控制功能main
parent
f79d622544
commit
d67da452a2
@ -0,0 +1,19 @@
|
||||
package com.ruoyi.asset.constant;
|
||||
|
||||
/**
|
||||
* 入库单状态
|
||||
*
|
||||
* @author Yangk
|
||||
*/
|
||||
public final class InboundOrderStatus
|
||||
{
|
||||
/** 草稿 */
|
||||
public static final String DRAFT = "DRAFT";
|
||||
|
||||
/** 已入库 */
|
||||
public static final String INBOUND_DONE = "INBOUND_DONE";
|
||||
|
||||
private InboundOrderStatus()
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,196 @@
|
||||
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.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrder;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetLocation;
|
||||
import com.ruoyi.asset.domain.AmsWarehouse;
|
||||
import com.ruoyi.asset.constant.AssetStatus;
|
||||
import com.ruoyi.asset.service.IAmsAssetLocationService;
|
||||
import com.ruoyi.asset.service.IAmsAssetService;
|
||||
import com.ruoyi.asset.service.IAmsInboundOrderService;
|
||||
import com.ruoyi.asset.service.IAmsWarehouseService;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 入库管理Controller
|
||||
*
|
||||
* @author Yangk
|
||||
* @date 2026-06-10
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/asset/inbound")
|
||||
public class AmsInboundOrderController extends BaseController
|
||||
{
|
||||
private static final String ENABLED_YES = "Y";
|
||||
|
||||
private String prefix = "asset/inbound";
|
||||
|
||||
@Autowired
|
||||
private IAmsInboundOrderService amsInboundOrderService;
|
||||
|
||||
@Autowired
|
||||
private IAmsWarehouseService amsWarehouseService;
|
||||
|
||||
@Autowired
|
||||
private IAmsAssetLocationService amsAssetLocationService;
|
||||
|
||||
@Autowired
|
||||
private IAmsAssetService amsAssetService;
|
||||
|
||||
@RequiresPermissions("asset:inbound:view")
|
||||
@GetMapping()
|
||||
public String inbound(ModelMap mmap)
|
||||
{
|
||||
mmap.put("warehouseList", selectEnabledWarehouseList());
|
||||
return prefix + "/inbound";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询入库管理列表
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:list")
|
||||
@PostMapping("/list")
|
||||
@ResponseBody
|
||||
public TableDataInfo list(AmsInboundOrder amsInboundOrder)
|
||||
{
|
||||
startPage();
|
||||
List<AmsInboundOrder> list = amsInboundOrderService.selectAmsInboundOrderList(amsInboundOrder);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出入库管理列表
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:export")
|
||||
@Log(title = "入库管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@ResponseBody
|
||||
public AjaxResult export(AmsInboundOrder amsInboundOrder)
|
||||
{
|
||||
List<AmsInboundOrder> list = amsInboundOrderService.selectAmsInboundOrderList(amsInboundOrder);
|
||||
ExcelUtil<AmsInboundOrder> util = new ExcelUtil<AmsInboundOrder>(AmsInboundOrder.class);
|
||||
return util.exportExcel(list, "入库管理数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看入库管理详情
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:view")
|
||||
@GetMapping("/view/{orderId}")
|
||||
public String view(@PathVariable("orderId") Long orderId, ModelMap mmap)
|
||||
{
|
||||
AmsInboundOrder amsInboundOrder = amsInboundOrderService.selectAmsInboundOrderByOrderId(orderId);
|
||||
mmap.put("amsInboundOrder", amsInboundOrder);
|
||||
return prefix + "/view";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增入库管理
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:add")
|
||||
@GetMapping("/add")
|
||||
public String add(ModelMap mmap)
|
||||
{
|
||||
putInboundOptions(mmap);
|
||||
return prefix + "/add";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增保存入库管理
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:add")
|
||||
@Log(title = "入库管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
@ResponseBody
|
||||
public AjaxResult addSave(AmsInboundOrder amsInboundOrder)
|
||||
{
|
||||
amsInboundOrder.setCreateBy(getLoginName());
|
||||
return toAjax(amsInboundOrderService.insertAmsInboundOrder(amsInboundOrder));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改入库管理
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:edit")
|
||||
@GetMapping("/edit/{orderId}")
|
||||
public String edit(@PathVariable("orderId") Long orderId, ModelMap mmap)
|
||||
{
|
||||
AmsInboundOrder amsInboundOrder = amsInboundOrderService.selectAmsInboundOrderByOrderId(orderId);
|
||||
mmap.put("amsInboundOrder", amsInboundOrder);
|
||||
putInboundOptions(mmap);
|
||||
return prefix + "/edit";
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存入库管理
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:edit")
|
||||
@Log(title = "入库管理", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/edit")
|
||||
@ResponseBody
|
||||
public AjaxResult editSave(AmsInboundOrder amsInboundOrder)
|
||||
{
|
||||
amsInboundOrder.setUpdateBy(getLoginName());
|
||||
return toAjax(amsInboundOrderService.updateAmsInboundOrder(amsInboundOrder));
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认入库
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:confirm")
|
||||
@Log(title = "入库管理", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/confirm/{orderId}")
|
||||
@ResponseBody
|
||||
public AjaxResult confirm(@PathVariable("orderId") Long orderId)
|
||||
{
|
||||
return toAjax(amsInboundOrderService.confirmInbound(orderId, getUserId(),
|
||||
getSysUser().getUserName(), getLoginName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除入库管理
|
||||
*/
|
||||
@RequiresPermissions("asset:inbound:remove")
|
||||
@Log(title = "入库管理", businessType = BusinessType.DELETE)
|
||||
@PostMapping( "/remove")
|
||||
@ResponseBody
|
||||
public AjaxResult remove(String ids)
|
||||
{
|
||||
return toAjax(amsInboundOrderService.deleteAmsInboundOrderByOrderIds(ids));
|
||||
}
|
||||
|
||||
private void putInboundOptions(ModelMap mmap)
|
||||
{
|
||||
mmap.put("warehouseList", selectEnabledWarehouseList());
|
||||
|
||||
AmsAssetLocation location = new AmsAssetLocation();
|
||||
location.setEnabled(ENABLED_YES);
|
||||
mmap.put("locationList", amsAssetLocationService.selectAmsAssetLocationList(location));
|
||||
|
||||
AmsAsset asset = new AmsAsset();
|
||||
asset.setAssetStatus(AssetStatus.IN_STOCK);
|
||||
mmap.put("assetList", amsAssetService.selectAmsAssetList(asset));
|
||||
}
|
||||
|
||||
private List<AmsWarehouse> selectEnabledWarehouseList()
|
||||
{
|
||||
AmsWarehouse warehouse = new AmsWarehouse();
|
||||
warehouse.setEnabled(ENABLED_YES);
|
||||
return amsWarehouseService.selectAmsWarehouseList(warehouse);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
package com.ruoyi.asset.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrder;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrderItem;
|
||||
|
||||
/**
|
||||
* 入库管理Mapper接口
|
||||
*
|
||||
* @author Yangk
|
||||
* @date 2026-06-10
|
||||
*/
|
||||
public interface AmsInboundOrderMapper
|
||||
{
|
||||
/**
|
||||
* 查询入库管理
|
||||
*
|
||||
* @param orderId 入库管理主键
|
||||
* @return 入库管理
|
||||
*/
|
||||
public AmsInboundOrder selectAmsInboundOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 锁定并查询入库单
|
||||
*
|
||||
* @param orderId 入库单ID
|
||||
* @return 入库单
|
||||
*/
|
||||
public AmsInboundOrder selectAmsInboundOrderByOrderIdForUpdate(Long orderId);
|
||||
|
||||
/**
|
||||
* 查询入库管理列表
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 入库管理集合
|
||||
*/
|
||||
public List<AmsInboundOrder> selectAmsInboundOrderList(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 新增入库管理
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertAmsInboundOrder(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 修改入库管理
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateAmsInboundOrder(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 确认入库并写入操作人信息
|
||||
*
|
||||
* @param amsInboundOrder 入库单
|
||||
* @return 结果
|
||||
*/
|
||||
public int confirmAmsInboundOrder(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 删除入库管理
|
||||
*
|
||||
* @param orderId 入库管理主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 批量删除入库管理
|
||||
*
|
||||
* @param orderIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderByOrderIds(String[] orderIds);
|
||||
|
||||
/**
|
||||
* 批量删除入库单明细
|
||||
*
|
||||
* @param orderIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderItemByOrderIds(String[] orderIds);
|
||||
|
||||
/**
|
||||
* 批量新增入库单明细
|
||||
*
|
||||
* @param amsInboundOrderItemList 入库单明细列表
|
||||
* @return 结果
|
||||
*/
|
||||
public int batchAmsInboundOrderItem(List<AmsInboundOrderItem> amsInboundOrderItemList);
|
||||
|
||||
|
||||
/**
|
||||
* 通过入库管理主键删除入库单明细信息
|
||||
*
|
||||
* @param orderId 入库管理ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderItemByOrderId(Long orderId);
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.ruoyi.asset.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrder;
|
||||
|
||||
/**
|
||||
* 入库管理Service接口
|
||||
*
|
||||
* @author Yangk
|
||||
* @date 2026-06-10
|
||||
*/
|
||||
public interface IAmsInboundOrderService
|
||||
{
|
||||
/**
|
||||
* 查询入库管理
|
||||
*
|
||||
* @param orderId 入库管理主键
|
||||
* @return 入库管理
|
||||
*/
|
||||
public AmsInboundOrder selectAmsInboundOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 查询入库管理列表
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 入库管理集合
|
||||
*/
|
||||
public List<AmsInboundOrder> selectAmsInboundOrderList(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 新增入库管理
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertAmsInboundOrder(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 修改入库管理
|
||||
*
|
||||
* @param amsInboundOrder 入库管理
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateAmsInboundOrder(AmsInboundOrder amsInboundOrder);
|
||||
|
||||
/**
|
||||
* 确认入库
|
||||
*
|
||||
* @param orderId 入库单ID
|
||||
* @param operateUserId 操作用户ID
|
||||
* @param operateUserName 操作用户名称
|
||||
* @param operateLoginName 操作登录账号
|
||||
* @return 结果
|
||||
*/
|
||||
public int confirmInbound(Long orderId, Long operateUserId, String operateUserName, String operateLoginName);
|
||||
|
||||
/**
|
||||
* 批量删除入库管理
|
||||
*
|
||||
* @param orderIds 需要删除的入库管理主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderByOrderIds(String orderIds);
|
||||
|
||||
/**
|
||||
* 删除入库管理信息
|
||||
*
|
||||
* @param orderId 入库管理主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAmsInboundOrderByOrderId(Long orderId);
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
<?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.AmsInboundOrderMapper">
|
||||
|
||||
<resultMap type="AmsInboundOrder" id="AmsInboundOrderResult">
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="inboundNo" column="inbound_no" />
|
||||
<result property="warehouseId" column="warehouse_id" />
|
||||
<result property="warehouseCode" column="warehouse_code" />
|
||||
<result property="warehouseName" column="warehouse_name" />
|
||||
<result property="inboundUserId" column="inbound_user_id" />
|
||||
<result property="inboundUserName" column="inbound_user_name" />
|
||||
<result property="inboundTime" column="inbound_time" />
|
||||
<result property="orderStatus" column="order_status" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="AmsInboundOrderItemCollectionResult" type="AmsInboundOrder" extends="AmsInboundOrderResult">
|
||||
<collection property="amsInboundOrderItemList" ofType="AmsInboundOrderItem"
|
||||
column="order_id" select="selectAmsInboundOrderItemList" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="AmsInboundOrderItem" id="AmsInboundOrderItemResult">
|
||||
<result property="itemId" column="item_id" />
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="inboundNo" column="inbound_no" />
|
||||
<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="locationId" column="location_id" />
|
||||
<result property="locationCode" column="location_code" />
|
||||
<result property="locationName" column="location_name" />
|
||||
<result property="inboundQuantity" column="inbound_quantity" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectAmsInboundOrderVo">
|
||||
select order_id, inbound_no, warehouse_id, warehouse_code, warehouse_name,
|
||||
inbound_user_id, inbound_user_name, inbound_time, order_status,
|
||||
create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from ams_inbound_order
|
||||
</sql>
|
||||
|
||||
<select id="selectAmsInboundOrderList" parameterType="AmsInboundOrder" resultMap="AmsInboundOrderResult">
|
||||
<include refid="selectAmsInboundOrderVo"/>
|
||||
<where>
|
||||
del_flag = '0'
|
||||
<if test="inboundNo != null and inboundNo != ''">and inbound_no = #{inboundNo}</if>
|
||||
<if test="warehouseId != null">and warehouse_id = #{warehouseId}</if>
|
||||
<if test="inboundUserName != null and inboundUserName != ''">
|
||||
and inbound_user_name like concat('%', #{inboundUserName}, '%')
|
||||
</if>
|
||||
<if test="params.beginInboundTime != null and params.beginInboundTime != ''">
|
||||
and inbound_time >= #{params.beginInboundTime}
|
||||
</if>
|
||||
<if test="params.endInboundTime != null and params.endInboundTime != ''">
|
||||
and inbound_time < date_add(#{params.endInboundTime}, interval 1 day)
|
||||
</if>
|
||||
<if test="orderStatus != null and orderStatus != ''">and order_status = #{orderStatus}</if>
|
||||
<if test="assetCode != null and assetCode != ''">
|
||||
and exists (
|
||||
select 1 from ams_inbound_order_item item
|
||||
where item.order_id = ams_inbound_order.order_id
|
||||
and item.del_flag = '0'
|
||||
and item.asset_code like concat(#{assetCode}, '%')
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc, order_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectAmsInboundOrderByOrderId" parameterType="Long" resultMap="AmsInboundOrderItemCollectionResult">
|
||||
<include refid="selectAmsInboundOrderVo"/>
|
||||
where order_id = #{orderId} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<select id="selectAmsInboundOrderByOrderIdForUpdate" parameterType="Long"
|
||||
resultMap="AmsInboundOrderItemCollectionResult">
|
||||
<include refid="selectAmsInboundOrderVo"/>
|
||||
where order_id = #{orderId} and del_flag = '0'
|
||||
for update
|
||||
</select>
|
||||
|
||||
<select id="selectAmsInboundOrderItemList" resultMap="AmsInboundOrderItemResult">
|
||||
select item_id, order_id, inbound_no, asset_id, asset_code, asset_name, category_id,
|
||||
category_code, category_name, spec_model, brand, location_id, location_code,
|
||||
location_name, inbound_quantity, create_by, create_time, update_by, update_time,
|
||||
remark, del_flag
|
||||
from ams_inbound_order_item
|
||||
where order_id = #{order_id} and del_flag = '0'
|
||||
order by item_id
|
||||
</select>
|
||||
|
||||
<insert id="insertAmsInboundOrder" parameterType="AmsInboundOrder" useGeneratedKeys="true" keyProperty="orderId">
|
||||
insert into ams_inbound_order (
|
||||
inbound_no, warehouse_id, warehouse_code, warehouse_name, order_status,
|
||||
create_by, create_time, remark, del_flag
|
||||
) values (
|
||||
#{inboundNo}, #{warehouseId}, #{warehouseCode}, #{warehouseName}, #{orderStatus},
|
||||
#{createBy}, #{createTime}, #{remark}, #{delFlag}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateAmsInboundOrder" parameterType="AmsInboundOrder">
|
||||
update ams_inbound_order
|
||||
set warehouse_id = #{warehouseId},
|
||||
warehouse_code = #{warehouseCode},
|
||||
warehouse_name = #{warehouseName},
|
||||
update_by = #{updateBy},
|
||||
update_time = #{updateTime},
|
||||
remark = #{remark}
|
||||
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
|
||||
</update>
|
||||
|
||||
<update id="confirmAmsInboundOrder" parameterType="AmsInboundOrder">
|
||||
update ams_inbound_order
|
||||
set order_status = #{orderStatus},
|
||||
inbound_user_id = #{inboundUserId},
|
||||
inbound_user_name = #{inboundUserName},
|
||||
inbound_time = #{inboundTime},
|
||||
update_by = #{updateBy},
|
||||
update_time = #{updateTime}
|
||||
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
|
||||
</update>
|
||||
|
||||
<update id="deleteAmsInboundOrderByOrderId" parameterType="Long">
|
||||
update ams_inbound_order
|
||||
set del_flag = '1'
|
||||
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
|
||||
</update>
|
||||
|
||||
<update id="deleteAmsInboundOrderByOrderIds" parameterType="String">
|
||||
update ams_inbound_order set del_flag = '1' where order_id in
|
||||
<foreach item="orderId" collection="array" open="(" separator="," close=")">
|
||||
#{orderId}
|
||||
</foreach>
|
||||
and del_flag = '0' and order_status = 'DRAFT'
|
||||
</update>
|
||||
|
||||
<update id="deleteAmsInboundOrderItemByOrderIds" parameterType="String">
|
||||
update ams_inbound_order_item set del_flag = '1' where order_id in
|
||||
<foreach item="orderId" collection="array" open="(" separator="," close=")">
|
||||
#{orderId}
|
||||
</foreach>
|
||||
and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<update id="deleteAmsInboundOrderItemByOrderId" parameterType="Long">
|
||||
update ams_inbound_order_item
|
||||
set del_flag = '1'
|
||||
where order_id = #{orderId} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<insert id="batchAmsInboundOrderItem">
|
||||
insert into ams_inbound_order_item (
|
||||
order_id, inbound_no, asset_id, asset_code, asset_name, category_id, category_code,
|
||||
category_name, spec_model, brand, location_id, location_code, location_name,
|
||||
inbound_quantity, create_by, create_time, remark, del_flag
|
||||
) values
|
||||
<foreach item="item" collection="list" separator=",">
|
||||
(
|
||||
#{item.orderId}, #{item.inboundNo}, #{item.assetId}, #{item.assetCode},
|
||||
#{item.assetName}, #{item.categoryId}, #{item.categoryCode}, #{item.categoryName},
|
||||
#{item.specModel}, #{item.brand}, #{item.locationId}, #{item.locationCode},
|
||||
#{item.locationName}, #{item.inboundQuantity}, #{item.createBy}, #{item.createTime},
|
||||
#{item.remark}, #{item.delFlag}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,211 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:include="include :: header('新增入库单')" />
|
||||
<style type="text/css">
|
||||
table label.error { position: inherit; }
|
||||
select + label.error { z-index: 1; right: 40px; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="white-bg">
|
||||
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
|
||||
<form class="form-horizontal m" id="form-inbound-add">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">入库仓库:</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="warehouseId" name="warehouseId" class="form-control" required>
|
||||
<option value="">请选择入库仓库</option>
|
||||
<option th:each="warehouse : ${warehouseList}"
|
||||
th:value="${warehouse.warehouseId}"
|
||||
th:text="${warehouse.warehouseCode + ' - ' + warehouse.warehouseName}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">备注:</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea name="remark" maxlength="500" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="form-header h4">入库明细</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-white btn-sm" onclick="addRow()">
|
||||
<i class="fa fa-plus"> 增加</i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-white btn-sm" onclick="sub.delRow()">
|
||||
<i class="fa fa-minus"> 删除</i>
|
||||
</button>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<script th:inline="javascript">
|
||||
var prefix = ctx + "asset/inbound";
|
||||
var assetList = [[${assetList}]];
|
||||
var locationList = [[${locationList}]];
|
||||
|
||||
$("#form-inbound-add").validate({
|
||||
focusCleanup: true
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if ($("#bootstrap-table").bootstrapTable('getData').length === 0) {
|
||||
$.modal.alertWarning("请至少添加一条入库明细");
|
||||
return;
|
||||
}
|
||||
if ($.validate.form()) {
|
||||
$.operate.save(prefix + "/add", $('#form-inbound-add').serialize());
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
initDetailTable([]);
|
||||
$("#warehouseId").change(function() {
|
||||
if ($("#bootstrap-table").bootstrapTable('getData').length > 0) {
|
||||
$("#bootstrap-table").bootstrapTable('removeAll');
|
||||
$.modal.alertWarning("入库仓库已变更,请重新添加入库明细");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function initDetailTable(data) {
|
||||
var options = {
|
||||
data: data,
|
||||
pagination: false,
|
||||
showSearch: false,
|
||||
showRefresh: false,
|
||||
showToggle: false,
|
||||
showColumns: false,
|
||||
sidePagination: "client",
|
||||
columns: [{
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
align: 'center',
|
||||
title: "序号",
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.serialNumber(index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'assetId',
|
||||
title: '资产',
|
||||
formatter: function(value, row, index) {
|
||||
return buildAssetSelect(value, index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'locationId',
|
||||
title: '入库位置',
|
||||
formatter: function(value, row, index) {
|
||||
return buildLocationSelect(value, index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'inboundQuantity',
|
||||
align: 'center',
|
||||
title: '数量',
|
||||
formatter: function(value, row, index) {
|
||||
return '<input class="form-control" type="text" readonly name="amsInboundOrderItemList['
|
||||
+ index + '].inboundQuantity" value="1">';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '明细备注',
|
||||
formatter: function(value, row, index) {
|
||||
return buildInput("amsInboundOrderItemList[" + index + "].remark", value, 500);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
formatter: function(value, row, index) {
|
||||
var rowIndex = $.common.isNotEmpty(row.index) ? row.index : $.table.serialNumber(index);
|
||||
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="sub.delRowByIndex(\''
|
||||
+ rowIndex + '\')"><i class="fa fa-remove"></i>删除</a>';
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
if ($.common.isEmpty($("#warehouseId").val())) {
|
||||
$.modal.alertWarning("请先选择入库仓库");
|
||||
return;
|
||||
}
|
||||
var count = $("#bootstrap-table").bootstrapTable('getData').length;
|
||||
sub.addRow({
|
||||
index: $.table.serialNumber(count),
|
||||
assetId: "",
|
||||
locationId: "",
|
||||
inboundQuantity: 1,
|
||||
remark: ""
|
||||
});
|
||||
}
|
||||
|
||||
function buildAssetSelect(value, index) {
|
||||
var select = $("<select>").addClass("form-control").attr({
|
||||
name: "amsInboundOrderItemList[" + index + "].assetId",
|
||||
required: true
|
||||
});
|
||||
select.append($("<option>").val("").text("请选择资产"));
|
||||
$.each(assetList, function(i, asset) {
|
||||
var label = asset.assetCode + " - " + asset.assetName;
|
||||
if ($.common.isNotEmpty(asset.categoryName)) {
|
||||
label += " - " + asset.categoryName;
|
||||
}
|
||||
var option = $("<option>").val(asset.assetId).text(label);
|
||||
if (String(asset.assetId) === String(value)) {
|
||||
option.attr("selected", "selected");
|
||||
}
|
||||
select.append(option);
|
||||
});
|
||||
return select.prop("outerHTML");
|
||||
}
|
||||
|
||||
function buildLocationSelect(value, index) {
|
||||
var warehouseId = $("#warehouseId").val();
|
||||
var select = $("<select>").addClass("form-control").attr({
|
||||
name: "amsInboundOrderItemList[" + index + "].locationId",
|
||||
required: true
|
||||
});
|
||||
select.append($("<option>").val("").text("请选择入库位置"));
|
||||
$.each(locationList, function(i, location) {
|
||||
if (String(location.warehouseId) === String(warehouseId)) {
|
||||
var label = location.locationCode + " - " + location.locationName;
|
||||
var option = $("<option>").val(location.locationId).text(label);
|
||||
if (String(location.locationId) === String(value)) {
|
||||
option.attr("selected", "selected");
|
||||
}
|
||||
select.append(option);
|
||||
}
|
||||
});
|
||||
return select.prop("outerHTML");
|
||||
}
|
||||
|
||||
function buildInput(name, value, maxLength) {
|
||||
return $("<input>").addClass("form-control").attr({
|
||||
type: "text",
|
||||
name: name,
|
||||
maxlength: maxLength
|
||||
}).val(value || "").prop("outerHTML");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,224 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:include="include :: header('修改入库单')" />
|
||||
<style type="text/css">
|
||||
table label.error { position: inherit; }
|
||||
select + label.error { z-index: 1; right: 40px; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="white-bg">
|
||||
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
|
||||
<form class="form-horizontal m" id="form-inbound-edit" th:object="${amsInboundOrder}">
|
||||
<input name="orderId" th:field="*{orderId}" type="hidden">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">入库单号:</label>
|
||||
<div class="col-sm-8">
|
||||
<input th:value="*{inboundNo}" class="form-control" type="text" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">入库仓库:</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="warehouseId" name="warehouseId" class="form-control" required>
|
||||
<option value="">请选择入库仓库</option>
|
||||
<option th:each="warehouse : ${warehouseList}"
|
||||
th:value="${warehouse.warehouseId}"
|
||||
th:text="${warehouse.warehouseCode + ' - ' + warehouse.warehouseName}"
|
||||
th:selected="${warehouse.warehouseId == amsInboundOrder.warehouseId}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">备注:</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea name="remark" maxlength="500" class="form-control" rows="3">[[*{remark}]]</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="form-header h4">入库明细</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-white btn-sm" onclick="addRow()">
|
||||
<i class="fa fa-plus"> 增加</i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-white btn-sm" onclick="sub.delRow()">
|
||||
<i class="fa fa-minus"> 删除</i>
|
||||
</button>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<script th:inline="javascript">
|
||||
var prefix = ctx + "asset/inbound";
|
||||
var assetList = [[${assetList}]];
|
||||
var locationList = [[${locationList}]];
|
||||
var detailList = [[${amsInboundOrder.amsInboundOrderItemList}]];
|
||||
|
||||
$("#form-inbound-edit").validate({
|
||||
focusCleanup: true
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if ($("#bootstrap-table").bootstrapTable('getData').length === 0) {
|
||||
$.modal.alertWarning("请至少保留一条入库明细");
|
||||
return;
|
||||
}
|
||||
if ($.validate.form()) {
|
||||
$.operate.save(prefix + "/edit", $('#form-inbound-edit').serialize());
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
initDetailTable(detailList);
|
||||
$("#warehouseId").change(function() {
|
||||
if ($("#bootstrap-table").bootstrapTable('getData').length > 0) {
|
||||
$("#bootstrap-table").bootstrapTable('removeAll');
|
||||
$.modal.alertWarning("入库仓库已变更,请重新添加入库明细");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function initDetailTable(data) {
|
||||
var options = {
|
||||
data: data,
|
||||
pagination: false,
|
||||
showSearch: false,
|
||||
showRefresh: false,
|
||||
showToggle: false,
|
||||
showColumns: false,
|
||||
sidePagination: "client",
|
||||
columns: [{
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
align: 'center',
|
||||
title: "序号",
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.serialNumber(index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'assetId',
|
||||
title: '资产',
|
||||
formatter: function(value, row, index) {
|
||||
return buildAssetSelect(value, index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'locationId',
|
||||
title: '入库位置',
|
||||
formatter: function(value, row, index) {
|
||||
return buildLocationSelect(value, index);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'inboundQuantity',
|
||||
align: 'center',
|
||||
title: '数量',
|
||||
formatter: function(value, row, index) {
|
||||
return '<input class="form-control" type="text" readonly name="amsInboundOrderItemList['
|
||||
+ index + '].inboundQuantity" value="1">';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '明细备注',
|
||||
formatter: function(value, row, index) {
|
||||
return buildInput("amsInboundOrderItemList[" + index + "].remark", value, 500);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
formatter: function(value, row, index) {
|
||||
var rowIndex = $.common.isNotEmpty(row.index) ? row.index : $.table.serialNumber(index);
|
||||
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="sub.delRowByIndex(\''
|
||||
+ rowIndex + '\')"><i class="fa fa-remove"></i>删除</a>';
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
if ($.common.isEmpty($("#warehouseId").val())) {
|
||||
$.modal.alertWarning("请先选择入库仓库");
|
||||
return;
|
||||
}
|
||||
var count = $("#bootstrap-table").bootstrapTable('getData').length;
|
||||
sub.addRow({
|
||||
index: $.table.serialNumber(count),
|
||||
assetId: "",
|
||||
locationId: "",
|
||||
inboundQuantity: 1,
|
||||
remark: ""
|
||||
});
|
||||
}
|
||||
|
||||
function buildAssetSelect(value, index) {
|
||||
var select = $("<select>").addClass("form-control").attr({
|
||||
name: "amsInboundOrderItemList[" + index + "].assetId",
|
||||
required: true
|
||||
});
|
||||
select.append($("<option>").val("").text("请选择资产"));
|
||||
$.each(assetList, function(i, asset) {
|
||||
var label = asset.assetCode + " - " + asset.assetName;
|
||||
if ($.common.isNotEmpty(asset.categoryName)) {
|
||||
label += " - " + asset.categoryName;
|
||||
}
|
||||
var option = $("<option>").val(asset.assetId).text(label);
|
||||
if (String(asset.assetId) === String(value)) {
|
||||
option.attr("selected", "selected");
|
||||
}
|
||||
select.append(option);
|
||||
});
|
||||
return select.prop("outerHTML");
|
||||
}
|
||||
|
||||
function buildLocationSelect(value, index) {
|
||||
var warehouseId = $("#warehouseId").val();
|
||||
var select = $("<select>").addClass("form-control").attr({
|
||||
name: "amsInboundOrderItemList[" + index + "].locationId",
|
||||
required: true
|
||||
});
|
||||
select.append($("<option>").val("").text("请选择入库位置"));
|
||||
$.each(locationList, function(i, location) {
|
||||
if (String(location.warehouseId) === String(warehouseId)) {
|
||||
var label = location.locationCode + " - " + location.locationName;
|
||||
var option = $("<option>").val(location.locationId).text(label);
|
||||
if (String(location.locationId) === String(value)) {
|
||||
option.attr("selected", "selected");
|
||||
}
|
||||
select.append(option);
|
||||
}
|
||||
});
|
||||
return select.prop("outerHTML");
|
||||
}
|
||||
|
||||
function buildInput(name, value, maxLength) {
|
||||
return $("<input>").addClass("form-control").attr({
|
||||
type: "text",
|
||||
name: name,
|
||||
maxlength: maxLength
|
||||
}).val(value || "").prop("outerHTML");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,152 @@
|
||||
<!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 :: datetimepicker-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>
|
||||
<input type="text" name="inboundNo"/>
|
||||
</li>
|
||||
<li>
|
||||
<label>入库仓库:</label>
|
||||
<select name="warehouseId">
|
||||
<option value="">所有</option>
|
||||
<option th:each="warehouse : ${warehouseList}"
|
||||
th:value="${warehouse.warehouseId}"
|
||||
th:text="${warehouse.warehouseCode + ' - ' + warehouse.warehouseName}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label>入库人:</label>
|
||||
<input type="text" name="inboundUserName"/>
|
||||
</li>
|
||||
<li class="select-time">
|
||||
<label>入库时间:</label>
|
||||
<input type="text" class="time-input" id="startTime" placeholder="开始时间"
|
||||
name="params[beginInboundTime]"/>
|
||||
<span>-</span>
|
||||
<input type="text" class="time-input" id="endTime" placeholder="结束时间"
|
||||
name="params[endInboundTime]"/>
|
||||
</li>
|
||||
<li>
|
||||
<label>单据状态:</label>
|
||||
<select name="orderStatus" th:with="type=${@dict.getType('ams_inbound_status')}">
|
||||
<option value="">所有</option>
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}"
|
||||
th:value="${dict.dictValue}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label>资产编码:</label>
|
||||
<input type="text" name="assetCode"/>
|
||||
</li>
|
||||
<li>
|
||||
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()">
|
||||
<i class="fa fa-search"></i> 搜索
|
||||
</a>
|
||||
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()">
|
||||
<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-success" onclick="$.operate.add()" shiro:hasPermission="asset:inbound:add">
|
||||
<i class="fa fa-plus"></i> 添加
|
||||
</a>
|
||||
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="asset:inbound:export">
|
||||
<i class="fa fa-download"></i> 导出
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: datetimepicker-js" />
|
||||
<script th:inline="javascript">
|
||||
var editFlag = [[${@permission.hasPermi('asset:inbound:edit')}]];
|
||||
var removeFlag = [[${@permission.hasPermi('asset:inbound:remove')}]];
|
||||
var confirmFlag = [[${@permission.hasPermi('asset:inbound:confirm')}]];
|
||||
var orderStatusDatas = [[${@dict.getType('ams_inbound_status')}]];
|
||||
var prefix = ctx + "asset/inbound";
|
||||
|
||||
$(function() {
|
||||
var options = {
|
||||
url: prefix + "/list",
|
||||
viewUrl: prefix + "/view/{id}",
|
||||
createUrl: prefix + "/add",
|
||||
updateUrl: prefix + "/edit/{id}",
|
||||
removeUrl: prefix + "/remove",
|
||||
exportUrl: prefix + "/export",
|
||||
modalName: "入库单",
|
||||
columns: [{
|
||||
field: 'orderId',
|
||||
title: '单据ID',
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: 'inboundNo',
|
||||
title: '入库单号'
|
||||
},
|
||||
{
|
||||
field: 'warehouseName',
|
||||
title: '入库仓库'
|
||||
},
|
||||
{
|
||||
field: 'inboundUserName',
|
||||
title: '入库人'
|
||||
},
|
||||
{
|
||||
field: 'inboundTime',
|
||||
title: '入库时间'
|
||||
},
|
||||
{
|
||||
field: 'orderStatus',
|
||||
title: '单据状态',
|
||||
formatter: function(value) {
|
||||
return $.table.selectDictLabel(orderStatusDatas, value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
formatter: function(value, row) {
|
||||
var actions = [];
|
||||
actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="$.operate.view(\'' + row.orderId + '\')"><i class="fa fa-eye"></i>查看</a> ');
|
||||
if (row.orderStatus === "DRAFT") {
|
||||
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.orderId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
|
||||
actions.push('<a class="btn btn-primary btn-xs ' + confirmFlag + '" href="javascript:void(0)" onclick="confirmInbound(\'' + row.orderId + '\')"><i class="fa fa-check"></i>确认入库</a> ');
|
||||
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.orderId + '\')"><i class="fa fa-remove"></i>删除</a>');
|
||||
}
|
||||
return actions.join('');
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
});
|
||||
|
||||
function confirmInbound(orderId) {
|
||||
$.modal.confirm("确认入库后将更新资产仓库与位置,且单据不可再修改。是否继续?", function() {
|
||||
$.operate.post(prefix + "/confirm/" + orderId, {});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:include="include :: header('入库单详情')" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-content">
|
||||
<form class="form-horizontal" th:object="${amsInboundOrder}">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">入库单号:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext" th:text="*{inboundNo}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">入库仓库:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext" th:text="*{warehouseCode + ' - ' + warehouseName}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">单据状态:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext" th:text="*{@dict.getLabel('ams_inbound_status', orderStatus)}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">入库人:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext" th:text="*{inboundUserName}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">入库时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext"
|
||||
th:text="*{inboundTime == null ? '' : #dates.format(inboundTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">备注:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-plaintext" th:text="*{remark}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="form-header h4">入库明细</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>资产编码</th>
|
||||
<th>资产名称</th>
|
||||
<th>资产类别</th>
|
||||
<th>规格型号</th>
|
||||
<th>品牌</th>
|
||||
<th>入库位置</th>
|
||||
<th>数量</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="item, stat : *{amsInboundOrderItemList}">
|
||||
<td th:text="${stat.count}"></td>
|
||||
<td th:text="${item.assetCode}"></td>
|
||||
<td th:text="${item.assetName}"></td>
|
||||
<td th:text="${item.categoryName}"></td>
|
||||
<td th:text="${item.specModel}"></td>
|
||||
<td th:text="${item.brand}"></td>
|
||||
<td th:text="${item.locationCode + ' - ' + item.locationName}"></td>
|
||||
<td th:text="${item.inboundQuantity}"></td>
|
||||
<td th:text="${item.remark}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,205 @@
|
||||
package com.ruoyi.asset.service.impl;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.ruoyi.asset.constant.AssetStatus;
|
||||
import com.ruoyi.asset.constant.InboundOrderStatus;
|
||||
import com.ruoyi.asset.domain.AmsAsset;
|
||||
import com.ruoyi.asset.domain.AmsAssetLocation;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrder;
|
||||
import com.ruoyi.asset.domain.AmsInboundOrderItem;
|
||||
import com.ruoyi.asset.domain.AmsWarehouse;
|
||||
import com.ruoyi.asset.domain.AssetTransitionContext;
|
||||
import com.ruoyi.asset.mapper.AmsInboundOrderMapper;
|
||||
import com.ruoyi.asset.service.IAmsAssetLocationService;
|
||||
import com.ruoyi.asset.service.IAmsAssetService;
|
||||
import com.ruoyi.asset.service.IAmsWarehouseService;
|
||||
import com.ruoyi.asset.service.IAssetStatusTransitionService;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.system.service.ISysCodeRuleService;
|
||||
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 AmsInboundOrderServiceImplTest
|
||||
{
|
||||
@Mock
|
||||
private AmsInboundOrderMapper amsInboundOrderMapper;
|
||||
|
||||
@Mock
|
||||
private ISysCodeRuleService sysCodeRuleService;
|
||||
|
||||
@Mock
|
||||
private IAmsWarehouseService amsWarehouseService;
|
||||
|
||||
@Mock
|
||||
private IAmsAssetLocationService amsAssetLocationService;
|
||||
|
||||
@Mock
|
||||
private IAmsAssetService amsAssetService;
|
||||
|
||||
@Mock
|
||||
private IAssetStatusTransitionService assetStatusTransitionService;
|
||||
|
||||
@InjectMocks
|
||||
private AmsInboundOrderServiceImpl service;
|
||||
|
||||
/** 新增草稿时由服务层生成单号,并从主数据回填所有快照。 */
|
||||
@Test
|
||||
void insertShouldGenerateCodeAndFillSnapshots()
|
||||
{
|
||||
AmsInboundOrder order = buildDraftRequest();
|
||||
when(sysCodeRuleService.nextCode("INBOUND_ORDER")).thenReturn("RK202606100001");
|
||||
stubMasterData();
|
||||
doAnswer(invocation -> {
|
||||
AmsInboundOrder inserted = invocation.getArgument(0);
|
||||
inserted.setOrderId(100L);
|
||||
return 1;
|
||||
}).when(amsInboundOrderMapper).insertAmsInboundOrder(any(AmsInboundOrder.class));
|
||||
when(amsInboundOrderMapper.batchAmsInboundOrderItem(anyList()))
|
||||
.thenAnswer(invocation -> ((List<?>) invocation.getArgument(0)).size());
|
||||
|
||||
int rows = service.insertAmsInboundOrder(order);
|
||||
|
||||
assertEquals(1, rows);
|
||||
assertEquals(100L, order.getOrderId());
|
||||
assertEquals("RK202606100001", order.getInboundNo());
|
||||
assertEquals(InboundOrderStatus.DRAFT, order.getOrderStatus());
|
||||
assertEquals("WH-002", order.getWarehouseCode());
|
||||
assertEquals("二号仓", order.getWarehouseName());
|
||||
AmsInboundOrderItem item = order.getAmsInboundOrderItemList().get(0);
|
||||
assertEquals(100L, item.getOrderId());
|
||||
assertEquals("ASSET-001", item.getAssetCode());
|
||||
assertEquals("测试资产", item.getAssetName());
|
||||
assertEquals("LOC-020", item.getLocationCode());
|
||||
assertEquals(1, item.getInboundQuantity());
|
||||
assertEquals("0", item.getDelFlag());
|
||||
assertNotNull(item.getCreateTime());
|
||||
}
|
||||
|
||||
/** 已完成入库单必须拒绝修改,避免绕过页面直接篡改单据。 */
|
||||
@Test
|
||||
void updateShouldRejectCompletedOrder()
|
||||
{
|
||||
AmsInboundOrder completed = new AmsInboundOrder();
|
||||
completed.setOrderId(100L);
|
||||
completed.setOrderStatus(InboundOrderStatus.INBOUND_DONE);
|
||||
when(amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(100L)).thenReturn(completed);
|
||||
|
||||
AmsInboundOrder request = new AmsInboundOrder();
|
||||
request.setOrderId(100L);
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> service.updateAmsInboundOrder(request));
|
||||
|
||||
assertTrue(exception.getMessage().contains("仅草稿"));
|
||||
verify(amsInboundOrderMapper, never()).updateAmsInboundOrder(any(AmsInboundOrder.class));
|
||||
}
|
||||
|
||||
/** 确认入库必须逐条调用公共状态流转服务,并完成单据确认信息。 */
|
||||
@Test
|
||||
void confirmShouldDelegateTransitionAndCompleteOrder()
|
||||
{
|
||||
AmsInboundOrder order = new AmsInboundOrder();
|
||||
order.setOrderId(100L);
|
||||
order.setInboundNo("RK202606100001");
|
||||
order.setWarehouseId(2L);
|
||||
order.setOrderStatus(InboundOrderStatus.DRAFT);
|
||||
AmsInboundOrderItem item = new AmsInboundOrderItem();
|
||||
item.setItemId(101L);
|
||||
item.setAssetId(1L);
|
||||
item.setLocationId(20L);
|
||||
order.setAmsInboundOrderItemList(Collections.singletonList(item));
|
||||
when(amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(100L)).thenReturn(order);
|
||||
when(amsInboundOrderMapper.confirmAmsInboundOrder(any(AmsInboundOrder.class))).thenReturn(1);
|
||||
|
||||
int rows = service.confirmInbound(100L, 1L, "管理员", "admin");
|
||||
|
||||
assertEquals(1, rows);
|
||||
ArgumentCaptor<AssetTransitionContext> contextCaptor = ArgumentCaptor.forClass(AssetTransitionContext.class);
|
||||
verify(assetStatusTransitionService).confirmInbound(
|
||||
org.mockito.ArgumentMatchers.eq(1L),
|
||||
org.mockito.ArgumentMatchers.eq(2L),
|
||||
org.mockito.ArgumentMatchers.eq(20L),
|
||||
contextCaptor.capture());
|
||||
assertEquals(100L, contextCaptor.getValue().getSourceOrderId());
|
||||
assertEquals(101L, contextCaptor.getValue().getSourceItemId());
|
||||
assertEquals("admin", contextCaptor.getValue().getOperateLoginName());
|
||||
assertEquals(InboundOrderStatus.INBOUND_DONE, order.getOrderStatus());
|
||||
assertEquals(1L, order.getInboundUserId());
|
||||
assertEquals("管理员", order.getInboundUserName());
|
||||
assertNotNull(order.getInboundTime());
|
||||
}
|
||||
|
||||
/** 已完成入库单必须拒绝删除。 */
|
||||
@Test
|
||||
void deleteShouldRejectCompletedOrder()
|
||||
{
|
||||
AmsInboundOrder completed = new AmsInboundOrder();
|
||||
completed.setOrderId(100L);
|
||||
completed.setOrderStatus(InboundOrderStatus.INBOUND_DONE);
|
||||
when(amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(100L)).thenReturn(completed);
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> service.deleteAmsInboundOrderByOrderId(100L));
|
||||
|
||||
assertTrue(exception.getMessage().contains("仅草稿"));
|
||||
verify(amsInboundOrderMapper, never()).deleteAmsInboundOrderByOrderId(100L);
|
||||
}
|
||||
|
||||
private AmsInboundOrder buildDraftRequest()
|
||||
{
|
||||
AmsInboundOrder order = new AmsInboundOrder();
|
||||
order.setWarehouseId(2L);
|
||||
order.setCreateBy("admin");
|
||||
AmsInboundOrderItem item = new AmsInboundOrderItem();
|
||||
item.setAssetId(1L);
|
||||
item.setLocationId(20L);
|
||||
order.setAmsInboundOrderItemList(Collections.singletonList(item));
|
||||
return order;
|
||||
}
|
||||
|
||||
private void stubMasterData()
|
||||
{
|
||||
AmsWarehouse warehouse = new AmsWarehouse();
|
||||
warehouse.setWarehouseId(2L);
|
||||
warehouse.setWarehouseCode("WH-002");
|
||||
warehouse.setWarehouseName("二号仓");
|
||||
warehouse.setEnabled("Y");
|
||||
when(amsWarehouseService.selectAmsWarehouseByWarehouseId(2L)).thenReturn(warehouse);
|
||||
|
||||
AmsAsset asset = new AmsAsset();
|
||||
asset.setAssetId(1L);
|
||||
asset.setAssetCode("ASSET-001");
|
||||
asset.setAssetName("测试资产");
|
||||
asset.setCategoryId(3L);
|
||||
asset.setCategoryCode("CAT-003");
|
||||
asset.setCategoryName("测试类别");
|
||||
asset.setSpecModel("SPEC-001");
|
||||
asset.setBrand("测试品牌");
|
||||
asset.setAssetStatus(AssetStatus.IN_STOCK);
|
||||
when(amsAssetService.selectAmsAssetByAssetId(1L)).thenReturn(asset);
|
||||
|
||||
AmsAssetLocation location = new AmsAssetLocation();
|
||||
location.setLocationId(20L);
|
||||
location.setWarehouseId(2L);
|
||||
location.setLocationCode("LOC-020");
|
||||
location.setLocationName("二号仓A区");
|
||||
location.setEnabled("Y");
|
||||
when(amsAssetLocationService.selectAmsAssetLocationByLocationId(20L)).thenReturn(location);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue