feat(asset): 优化调拨单新增页面和后端控制器

- 更新页面标题为新增调拨单
- 添加表单验证样式规则
- 重构基本信息表单布局结构
- 实现资产选择功能替换原来的静态行添加
- 添加调拨明细表格的动态操作功能
- 集成仓库、位置、部门和用户选择下拉框
- 添加资产状态数据字典支持
- 实现调拨单实体类注释完善和字段调整
- 增加可调拨资产选择器接口支持
- 优化控制器方法参数和返回处理
- 添加权限控制和数据验证机制
main
yangk 2 weeks ago
parent ebb710c43c
commit 3bb8cff513

@ -0,0 +1,22 @@
package com.ruoyi.asset.constant;
/**
*
*
* @author Yangk
*/
public final class TransferOrderStatus
{
/** 草稿 */
public static final String DRAFT = "DRAFT";
/** 待确认(已提交,等待确认人确认调拨) */
public static final String PENDING_CONFIRM = "PENDING_CONFIRM";
/** 已调拨(确认完成,资产归属已更新) */
public static final String TRANSFERRED = "TRANSFERRED";
private TransferOrderStatus()
{
}
}

@ -1,6 +1,25 @@
package com.ruoyi.asset.controller;
import java.util.List;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsTransferOrderService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@ -9,54 +28,74 @@ 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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.service.IAmsTransferOrderService;
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-12
*/
@Controller
@RequestMapping("/asset/transfer")
public class AmsTransferOrderController extends BaseController
{
private static final String ENABLED_YES = "Y";
private String prefix = "asset/transfer";
@Autowired
private IAmsTransferOrderService amsTransferOrderService;
@Autowired
private IAmsWarehouseService amsWarehouseService;
@Autowired
private IAmsAssetLocationService amsAssetLocationService;
@Autowired
private ISysDeptService sysDeptService;
@Autowired
private ISysUserService sysUserService;
@RequiresPermissions("asset:transfer:view")
@GetMapping()
public String transfer()
public String transfer(ModelMap mmap)
{
mmap.put("warehouseList", selectEnabledWarehouseList());
return prefix + "/transfer";
}
/**
*
*/
@RequiresPermissions("asset:transfer:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(AmsTransferOrder amsTransferOrder)
{
startPage();
List<AmsTransferOrder> list = amsTransferOrderService.selectAmsTransferOrderList(amsTransferOrder);
return getDataTable(list);
return getDataTable(amsTransferOrderService.selectAmsTransferOrderList(amsTransferOrder));
}
/** 打开可调拨资产分页选择页(调拨单新增/编辑的明细弹窗选择器) */
@RequiresPermissions(value = { "asset:transfer:add", "asset:transfer:edit" }, logical = Logical.OR)
@GetMapping("/selectAsset")
public String selectAsset(@RequestParam(value = "orderId", required = false) Long orderId, ModelMap mmap)
{
mmap.put("orderId", orderId);
return prefix + "/selectAsset";
}
/** 分页查询未被其他有效调拨单占用的在库/在用资产(仅返回可调拨资产) */
@RequiresPermissions(value = { "asset:transfer:add", "asset:transfer:edit" }, logical = Logical.OR)
@PostMapping("/availableAssetList")
@ResponseBody
public TableDataInfo availableAssetList(AmsAsset amsAsset,
@RequestParam(value = "orderId", required = false) Long orderId)
{
startPage();
return getDataTable(amsTransferOrderService.selectAvailableTransferAssetList(amsAsset, orderId));
}
/**
*
*/
@RequiresPermissions("asset:transfer:export")
@Log(title = "调拨管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ -64,77 +103,107 @@ public class AmsTransferOrderController extends BaseController
public AjaxResult export(AmsTransferOrder amsTransferOrder)
{
List<AmsTransferOrder> list = amsTransferOrderService.selectAmsTransferOrderList(amsTransferOrder);
ExcelUtil<AmsTransferOrder> util = new ExcelUtil<AmsTransferOrder>(AmsTransferOrder.class);
return util.exportExcel(list, "调拨管理数据");
return new ExcelUtil<AmsTransferOrder>(AmsTransferOrder.class).exportExcel(list, "调拨管理数据");
}
/**
*
*/
@RequiresPermissions("asset:transfer:view")
@GetMapping("/view/{orderId}")
public String view(@PathVariable("orderId") Long orderId, ModelMap mmap)
{
AmsTransferOrder amsTransferOrder = amsTransferOrderService.selectAmsTransferOrderByOrderId(orderId);
mmap.put("amsTransferOrder", amsTransferOrder);
mmap.put("amsTransferOrder", amsTransferOrderService.selectAmsTransferOrderByOrderId(orderId));
return prefix + "/view";
}
/**
*
*/
@RequiresPermissions("asset:transfer:add")
@GetMapping("/add")
public String add()
public String add(ModelMap mmap)
{
putTransferOptions(mmap);
return prefix + "/add";
}
/**
*
*/
@RequiresPermissions("asset:transfer:add")
@Log(title = "调拨管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(AmsTransferOrder amsTransferOrder)
{
return toAjax(amsTransferOrderService.insertAmsTransferOrder(amsTransferOrder));
amsTransferOrder.setCreateBy(getLoginName());
return toAjax(amsTransferOrderService.insertAmsTransferOrder(amsTransferOrder,
getUserId(), getSysUser().getUserName()));
}
/**
*
*/
@RequiresPermissions("asset:transfer:edit")
@GetMapping("/edit/{orderId}")
public String edit(@PathVariable("orderId") Long orderId, ModelMap mmap)
{
AmsTransferOrder amsTransferOrder = amsTransferOrderService.selectAmsTransferOrderByOrderId(orderId);
mmap.put("amsTransferOrder", amsTransferOrder);
mmap.put("amsTransferOrder", amsTransferOrderService.selectAmsTransferOrderByOrderId(orderId));
putTransferOptions(mmap);
return prefix + "/edit";
}
/**
*
*/
@RequiresPermissions("asset:transfer:edit")
@Log(title = "调拨管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(AmsTransferOrder amsTransferOrder)
{
amsTransferOrder.setUpdateBy(getLoginName());
return toAjax(amsTransferOrderService.updateAmsTransferOrder(amsTransferOrder));
}
/**
*
*/
/** 提交调拨单DRAFT → PENDING_CONFIRM提交后不可再修改或删除。 */
@RequiresPermissions("asset:transfer:submit")
@Log(title = "调拨管理", businessType = BusinessType.UPDATE)
@PostMapping("/submit/{orderId}")
@ResponseBody
public AjaxResult submit(@PathVariable("orderId") Long orderId)
{
return toAjax(amsTransferOrderService.submitTransfer(orderId, getLoginName()));
}
/** 确认调拨PENDING_CONFIRM → TRANSFERRED确认后更新全部资产的当前归属并写入履历。 */
@RequiresPermissions("asset:transfer:confirm")
@Log(title = "调拨管理", businessType = BusinessType.UPDATE)
@PostMapping("/confirm/{orderId}")
@ResponseBody
public AjaxResult confirm(@PathVariable("orderId") Long orderId)
{
return toAjax(amsTransferOrderService.confirmTransfer(orderId, getUserId(),
getSysUser().getUserName(), getLoginName()));
}
@RequiresPermissions("asset:transfer:remove")
@Log(title = "调拨管理", businessType = BusinessType.DELETE)
@PostMapping( "/remove")
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
return toAjax(amsTransferOrderService.deleteAmsTransferOrderByOrderIds(ids));
}
/** 调拨表单需要仓库、位置、部门、用户四组下拉选项 */
private void putTransferOptions(ModelMap mmap)
{
mmap.put("warehouseList", selectEnabledWarehouseList());
AmsAssetLocation location = new AmsAssetLocation();
location.setEnabled(ENABLED_YES);
mmap.put("locationList", amsAssetLocationService.selectAmsAssetLocationList(location));
SysDept dept = new SysDept();
dept.setStatus(UserConstants.DEPT_NORMAL);
mmap.put("deptList", sysDeptService.selectDeptList(dept));
SysUser user = new SysUser();
user.setStatus(UserConstants.NORMAL);
mmap.put("userList", sysUserService.selectUserList(user));
}
private List<AmsWarehouse> selectEnabledWarehouseList()
{
AmsWarehouse warehouse = new AmsWarehouse();
warehouse.setEnabled(ENABLED_YES);
return amsWarehouseService.selectAmsWarehouseList(warehouse);
}
}

@ -25,33 +25,31 @@ public class AmsTransferOrder extends BaseEntity
@Excel(name = "调拨单号")
private String transferNo;
/** 申请人ID */
@Excel(name = "申请人ID")
/** 申请人ID新增时由服务层从登录用户取值前端不传 */
private Long applicantId;
/** 申请人名称快照 */
@Excel(name = "申请人名称快照")
/** 申请人名称快照(保存时冻结,后续姓名变更不影响历史记录) */
@Excel(name = "申请人")
private String applicantName;
/** 调拨原因 */
@Excel(name = "调拨原因")
private String transferReason;
/** 确认人ID */
@Excel(name = "确认人ID")
/** 确认人ID确认调拨时由服务层写入 */
private Long confirmUserId;
/** 确认人名称快照 */
@Excel(name = "确认人名称快照")
/** 确认人名称快照(确认时冻结) */
@Excel(name = "确认人")
private String confirmUserName;
/** 确认时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "确认时间", width = 30, dateFormat = "yyyy-MM-dd")
/** 确认时间(确认调拨时由服务层写入当前时间) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Excel(name = "确认时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date confirmTime;
/** 单据状态 */
@Excel(name = "单据状态")
/** 单据状态DRAFT / PENDING_CONFIRM / TRANSFERRED由服务层管控状态流转 */
@Excel(name = "单据状态", dictType = "ams_transfer_status")
private String orderStatus;
/** 删除标志0存在1删除 */

@ -54,10 +54,13 @@ public class AmsTransferOrderItem extends BaseEntity
@Excel(name = "规格型号快照")
private String specModel;
/** 品牌快照 */
/** 品牌快照(保存时从资产台账回填) */
@Excel(name = "品牌快照")
private String brand;
/** 当前资产状态,仅用于调拨表单展示和校验,不写入调拨明细表(由 LEFT JOIN 实时查询) */
private String assetStatus;
/** 原部门ID */
@Excel(name = "原部门ID")
private Long oldDeptId;
@ -240,6 +243,15 @@ public class AmsTransferOrderItem extends BaseEntity
{
return brand;
}
public void setAssetStatus(String assetStatus)
{
this.assetStatus = assetStatus;
}
public String getAssetStatus()
{
return assetStatus;
}
public void setOldDeptId(Long oldDeptId)
{
this.oldDeptId = oldDeptId;
@ -444,6 +456,7 @@ public class AmsTransferOrderItem extends BaseEntity
.append("categoryName", getCategoryName())
.append("specModel", getSpecModel())
.append("brand", getBrand())
.append("assetStatus", getAssetStatus())
.append("oldDeptId", getOldDeptId())
.append("oldDeptName", getOldDeptName())
.append("newDeptId", getNewDeptId())

@ -1,6 +1,8 @@
package com.ruoyi.asset.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.domain.AmsTransferOrderItem;
@ -20,6 +22,11 @@ public interface AmsTransferOrderMapper
*/
public AmsTransferOrder selectAmsTransferOrderByOrderId(Long orderId);
/**
* SELECT ... FOR UPDATE
*/
public AmsTransferOrder selectAmsTransferOrderByOrderIdForUpdate(Long orderId);
/**
*
*
@ -28,6 +35,24 @@ public interface AmsTransferOrderMapper
*/
public List<AmsTransferOrder> selectAmsTransferOrderList(AmsTransferOrder amsTransferOrder);
/**
* 使
* <p> IN_STOCK IN_USE 稿</p>
*/
public List<AmsAsset> selectAvailableTransferAssetList(@Param("asset") AmsAsset amsAsset,
@Param("currentOrderId") Long currentOrderId, @Param("stockStatus") String stockStatus,
@Param("inUseStatus") String inUseStatus, @Param("draftStatus") String draftStatus,
@Param("pendingStatus") String pendingStatus);
/**
* 稿+
* <p> selectAmsAssetByAssetIdForUpdate 使
* </p>
*/
public int countOtherActiveTransferOrderByAssetId(@Param("assetId") Long assetId,
@Param("currentOrderId") Long currentOrderId, @Param("draftStatus") String draftStatus,
@Param("pendingStatus") String pendingStatus);
/**
*
*
@ -44,6 +69,12 @@ public interface AmsTransferOrderMapper
*/
public int updateAmsTransferOrder(AmsTransferOrder amsTransferOrder);
/** 提交调拨单DRAFT → PENDING_CONFIRM */
public int submitAmsTransferOrder(AmsTransferOrder amsTransferOrder);
/** 确认调拨单PENDING_CONFIRM → TRANSFERRED */
public int confirmAmsTransferOrder(AmsTransferOrder amsTransferOrder);
/**
*
*

@ -1,6 +1,7 @@
package com.ruoyi.asset.service;
import java.util.List;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsTransferOrder;
/**
@ -27,13 +28,24 @@ public interface IAmsTransferOrderService
*/
public List<AmsTransferOrder> selectAmsTransferOrderList(AmsTransferOrder amsTransferOrder);
/**
*
* <p> IN_STOCK IN_USE
* /使</p>
*
* @param amsAsset
* @param currentOrderId ID
* @return
*/
public List<AmsAsset> selectAvailableTransferAssetList(AmsAsset amsAsset, Long currentOrderId);
/**
*
*
* @param amsTransferOrder
* @return
*/
public int insertAmsTransferOrder(AmsTransferOrder amsTransferOrder);
public int insertAmsTransferOrder(AmsTransferOrder amsTransferOrder, Long applicantId, String applicantName);
/**
*
@ -43,6 +55,18 @@ public interface IAmsTransferOrderService
*/
public int updateAmsTransferOrder(AmsTransferOrder amsTransferOrder);
/**
* 稿
* <p>稿</p>
*/
public int submitTransfer(Long orderId, String operateLoginName);
/**
*
* <p></p>
*/
public int confirmTransfer(Long orderId, Long operateUserId, String operateUserName, String operateLoginName);
/**
*
*

@ -1,135 +1,602 @@
package com.ruoyi.asset.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.constant.TransferOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.domain.AmsTransferOrderItem;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsTransferOrderMapper;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsTransferOrderService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.asset.service.IAssetStatusTransitionService;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysCodeRuleService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.asset.domain.AmsTransferOrderItem;
import com.ruoyi.asset.mapper.AmsTransferOrderMapper;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.service.IAmsTransferOrderService;
import com.ruoyi.common.core.text.Convert;
/**
* Service
*
*
* @author Yangk
* @date 2026-06-12
*/
@Service
public class AmsTransferOrderServiceImpl implements IAmsTransferOrderService
public class AmsTransferOrderServiceImpl implements IAmsTransferOrderService
{
/** 编号规则业务标识 */
private static final String TRANSFER_ORDER_RULE = "TRANSFER_ORDER";
private static final String ENABLED_YES = "Y";
private static final String DEL_FLAG_NORMAL = "0";
@Autowired
private AmsTransferOrderMapper amsTransferOrderMapper;
/**
*
*
* @param orderId
* @return
*/
@Autowired
private AmsAssetMapper amsAssetMapper;
@Autowired
private ISysCodeRuleService sysCodeRuleService;
@Autowired
private IAmsWarehouseService amsWarehouseService;
@Autowired
private IAmsAssetLocationService amsAssetLocationService;
@Autowired
private ISysDeptService sysDeptService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private IAssetStatusTransitionService assetStatusTransitionService;
@Override
public AmsTransferOrder selectAmsTransferOrderByOrderId(Long orderId)
{
return amsTransferOrderMapper.selectAmsTransferOrderByOrderId(orderId);
}
/**
*
*
* @param amsTransferOrder
* @return
*/
@Override
public List<AmsTransferOrder> selectAmsTransferOrderList(AmsTransferOrder amsTransferOrder)
{
return amsTransferOrderMapper.selectAmsTransferOrderList(amsTransferOrder);
}
/**
*
*
* @param amsTransferOrder
* @return
*/
@Transactional
/** 查询可调拨资产候选列表,状态和占用条件由服务层固定传入,不开放给前端控制。 */
@Override
public int insertAmsTransferOrder(AmsTransferOrder amsTransferOrder)
public List<AmsAsset> selectAvailableTransferAssetList(AmsAsset amsAsset, Long currentOrderId)
{
amsTransferOrder.setCreateTime(DateUtils.getNowDate());
int rows = amsTransferOrderMapper.insertAmsTransferOrder(amsTransferOrder);
insertAmsTransferOrderItem(amsTransferOrder);
return amsTransferOrderMapper.selectAvailableTransferAssetList(amsAsset, currentOrderId,
AssetStatus.IN_STOCK, AssetStatus.IN_USE, TransferOrderStatus.DRAFT,
TransferOrderStatus.PENDING_CONFIRM);
}
/**
* 稿 +
* <p>Controller</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertAmsTransferOrder(AmsTransferOrder order, Long applicantId, String applicantName)
{
validateOrderRequest(order);
validateOperator(applicantId, applicantName, "申请人");
order.setTransferNo(sysCodeRuleService.nextCode(TRANSFER_ORDER_RULE));
order.setApplicantId(applicantId);
order.setApplicantName(StringUtils.trim(applicantName));
order.setOrderStatus(TransferOrderStatus.DRAFT);
order.setConfirmUserId(null);
order.setConfirmUserName(null);
order.setConfirmTime(null);
order.setDelFlag(DEL_FLAG_NORMAL);
order.setCreateTime(DateUtils.getNowDate());
fillOrderSnapshots(order, null);
if (amsTransferOrderMapper.insertAmsTransferOrder(order) != 1 || StringUtils.isNull(order.getOrderId()))
{
throw new ServiceException("调拨单保存失败");
}
insertTransferOrderItems(order);
return 1;
}
/**
* 稿
* <p></p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int updateAmsTransferOrder(AmsTransferOrder order)
{
if (StringUtils.isNull(order) || StringUtils.isNull(order.getOrderId()))
{
throw new ServiceException("调拨单ID不能为空");
}
AmsTransferOrder current = requireOrderForUpdate(order.getOrderId(), TransferOrderStatus.DRAFT,
"仅草稿调拨单允许修改");
validateOrderRequest(order);
// 单据号、申请人、状态和确认信息均由服务层维护,普通编辑不得篡改。
order.setTransferNo(current.getTransferNo());
order.setApplicantId(current.getApplicantId());
order.setApplicantName(current.getApplicantName());
order.setOrderStatus(current.getOrderStatus());
order.setConfirmUserId(current.getConfirmUserId());
order.setConfirmUserName(current.getConfirmUserName());
order.setConfirmTime(current.getConfirmTime());
order.setUpdateTime(DateUtils.getNowDate());
fillOrderSnapshots(order, order.getOrderId());
amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(order.getOrderId());
insertTransferOrderItems(order);
if (amsTransferOrderMapper.updateAmsTransferOrder(order) != 1)
{
throw new ServiceException("调拨单状态已变化,保存失败");
}
return 1;
}
/**
* 稿 稿
* <p>稿</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int submitTransfer(Long orderId, String operateLoginName)
{
AmsTransferOrder order = requireOrderForUpdate(orderId, TransferOrderStatus.DRAFT,
"仅草稿调拨单允许提交");
validateLoginName(operateLoginName);
validateOrderReadyForFlow(order);
order.setOrderStatus(TransferOrderStatus.PENDING_CONFIRM);
order.setUpdateBy(StringUtils.trim(operateLoginName));
order.setUpdateTime(DateUtils.getNowDate());
if (amsTransferOrderMapper.submitAmsTransferOrder(order) != 1)
{
throw new ServiceException("调拨单状态已变化,提交失败");
}
return 1;
}
/**
*
* <p></p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int confirmTransfer(Long orderId, Long operateUserId, String operateUserName, String operateLoginName)
{
AmsTransferOrder order = requireOrderForUpdate(orderId, TransferOrderStatus.PENDING_CONFIRM,
"仅待确认调拨单允许确认");
validateOperator(operateUserId, operateUserName, "确认人");
validateLoginName(operateLoginName);
validateOrderReadyForFlow(order);
List<AmsTransferOrderItem> sortedItems = sortedItems(order);
for (AmsTransferOrderItem item : sortedItems)
{
AssetTransitionContext context = new AssetTransitionContext();
context.setSourceOrderId(order.getOrderId());
context.setSourceOrderNo(order.getTransferNo());
context.setSourceItemId(item.getItemId());
context.setOperateUserId(operateUserId);
context.setOperateUserName(StringUtils.trim(operateUserName));
context.setOperateLoginName(StringUtils.trim(operateLoginName));
context.setChangeSummary("确认资产调拨");
context.setRemark(order.getRemark());
assetStatusTransitionService.confirmTransfer(item.getAssetId(), item.getNewWarehouseId(),
item.getNewLocationId(), item.getNewDeptId(), item.getNewUserId(), context);
}
Date now = DateUtils.getNowDate();
order.setOrderStatus(TransferOrderStatus.TRANSFERRED);
order.setConfirmUserId(operateUserId);
order.setConfirmUserName(StringUtils.trim(operateUserName));
order.setConfirmTime(now);
order.setUpdateBy(StringUtils.trim(operateLoginName));
order.setUpdateTime(now);
if (amsTransferOrderMapper.confirmAmsTransferOrder(order) != 1)
{
throw new ServiceException("调拨单状态已变化,确认调拨失败");
}
return 1;
}
/** 批量删除逐条加行锁校验草稿状态后删除按ID升序避免死锁。 */
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteAmsTransferOrderByOrderIds(String orderIds)
{
Long[] sortedIds = Arrays.stream(Convert.toStrArray(orderIds))
.map(Long::valueOf).sorted().toArray(Long[]::new);
int rows = 0;
for (Long orderId : sortedIds)
{
AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
if (StringUtils.isNull(order))
{
continue;
}
requireDraft(order);
amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(orderId);
rows += amsTransferOrderMapper.deleteAmsTransferOrderByOrderId(orderId);
}
return rows;
}
/**
*
*
* @param amsTransferOrder
* @return
*/
@Transactional
@Override
public int updateAmsTransferOrder(AmsTransferOrder amsTransferOrder)
{
amsTransferOrder.setUpdateTime(DateUtils.getNowDate());
amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(amsTransferOrder.getOrderId());
insertAmsTransferOrderItem(amsTransferOrder);
return amsTransferOrderMapper.updateAmsTransferOrder(amsTransferOrder);
}
/**
*
*
* @param orderIds
* @return
*/
@Transactional
@Override
public int deleteAmsTransferOrderByOrderIds(String orderIds)
{
amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderIds(Convert.toStrArray(orderIds));
return amsTransferOrderMapper.deleteAmsTransferOrderByOrderIds(Convert.toStrArray(orderIds));
}
/**
*
*
* @param orderId
* @return
*/
@Transactional
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteAmsTransferOrderByOrderId(Long orderId)
{
AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
if (StringUtils.isNull(order))
{
return 0;
}
requireDraft(order);
amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(orderId);
return amsTransferOrderMapper.deleteAmsTransferOrderByOrderId(orderId);
}
/**
*
*
* @param amsTransferOrder
*/
public void insertAmsTransferOrderItem(AmsTransferOrder amsTransferOrder)
private void validateOrderRequest(AmsTransferOrder order)
{
List<AmsTransferOrderItem> amsTransferOrderItemList = amsTransferOrder.getAmsTransferOrderItemList();
Long orderId = amsTransferOrder.getOrderId();
if (StringUtils.isNotNull(amsTransferOrderItemList))
if (StringUtils.isNull(order))
{
List<AmsTransferOrderItem> list = new ArrayList<AmsTransferOrderItem>();
for (AmsTransferOrderItem amsTransferOrderItem : amsTransferOrderItemList)
throw new ServiceException("调拨单不能为空");
}
if (order.getAmsTransferOrderItemList() == null || order.getAmsTransferOrderItemList().isEmpty())
{
throw new ServiceException("调拨单明细不能为空");
}
validateLength(order.getTransferReason(), 500, "调拨原因");
validateLength(order.getRemark(), 500, "备注");
}
/**
* 稿ID
*/
private void fillOrderSnapshots(AmsTransferOrder order, Long currentOrderId)
{
Set<Long> assetIds = new HashSet<>();
List<AmsTransferOrderItem> sortedItems = sortedItems(order);
order.setAmsTransferOrderItemList(sortedItems);
for (AmsTransferOrderItem item : sortedItems)
{
AmsAsset asset = lockTransferableAsset(item.getAssetId());
if (!assetIds.add(asset.getAssetId()))
{
amsTransferOrderItem.setOrderId(orderId);
list.add(amsTransferOrderItem);
throw new ServiceException("同一调拨单不能重复选择资产");
}
if (list.size() > 0)
validateNotOccupied(asset, currentOrderId);
fillAssetSnapshots(item, asset);
fillTargetSnapshots(item, asset);
validateActualChange(item, asset);
item.setTransferNo(order.getTransferNo());
item.setDelFlag(DEL_FLAG_NORMAL);
validateLength(item.getRemark(), 500, "明细备注");
}
}
/**
* 稿
*/
private void validateOrderReadyForFlow(AmsTransferOrder order)
{
List<AmsTransferOrderItem> items = sortedItems(order);
if (items.isEmpty())
{
throw new ServiceException("调拨单明细不能为空");
}
Set<Long> assetIds = new HashSet<>();
for (AmsTransferOrderItem item : items)
{
AmsAsset asset = lockTransferableAsset(item.getAssetId());
if (!assetIds.add(asset.getAssetId()))
{
amsTransferOrderMapper.batchAmsTransferOrderItem(list);
throw new ServiceException("同一调拨单不能重复选择资产");
}
validateNotOccupied(asset, order.getOrderId());
validateOldSnapshot(item, asset);
fillTargetSnapshots(item, asset);
validateActualChange(item, asset);
}
}
/** 加行锁并校验资产为可调拨状态(在库或在用),同时校验仓位和使用归属完整性。 */
private AmsAsset lockTransferableAsset(Long assetId)
{
if (StringUtils.isNull(assetId))
{
throw new ServiceException("调拨资产不能为空");
}
AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(assetId);
if (StringUtils.isNull(asset))
{
throw new ServiceException("调拨资产不存在或已删除");
}
if (!StringUtils.equalsAny(asset.getAssetStatus(), AssetStatus.IN_STOCK, AssetStatus.IN_USE))
{
throw new ServiceException(StringUtils.format("资产【{}】当前状态不允许调拨", asset.getAssetCode()));
}
if (StringUtils.isNull(asset.getWarehouseId()) || StringUtils.isNull(asset.getLocationId()))
{
throw new ServiceException(StringUtils.format("资产【{}】缺少当前仓库或位置", asset.getAssetCode()));
}
if (StringUtils.equals(AssetStatus.IN_USE, asset.getAssetStatus())
&& (StringUtils.isNull(asset.getUseDeptId()) || StringUtils.isNull(asset.getUseUserId())))
{
throw new ServiceException(StringUtils.format("在用资产【{}】缺少当前使用部门或使用人", asset.getAssetCode()));
}
return asset;
}
/** 校验资产未被其他有效调拨单(草稿+待确认)占用 */
private void validateNotOccupied(AmsAsset asset, Long currentOrderId)
{
if (amsTransferOrderMapper.countOtherActiveTransferOrderByAssetId(asset.getAssetId(), currentOrderId,
TransferOrderStatus.DRAFT, TransferOrderStatus.PENDING_CONFIRM) > 0)
{
throw new ServiceException(StringUtils.format("资产【{}】已被其他有效调拨单占用", asset.getAssetCode()));
}
}
/** 从资产台账回填「调拨前」的全部归属快照,确保明细记录完整的变更历史。 */
private void fillAssetSnapshots(AmsTransferOrderItem item, AmsAsset asset)
{
item.setAssetCode(asset.getAssetCode());
item.setAssetName(asset.getAssetName());
item.setCategoryId(asset.getCategoryId());
item.setCategoryCode(asset.getCategoryCode());
item.setCategoryName(asset.getCategoryName());
item.setSpecModel(asset.getSpecModel());
item.setBrand(asset.getBrand());
item.setAssetStatus(asset.getAssetStatus());
item.setOldDeptId(asset.getUseDeptId());
item.setOldDeptName(asset.getUseDeptName());
item.setOldUserId(asset.getUseUserId());
item.setOldUserName(asset.getUseUserName());
item.setOldWarehouseId(asset.getWarehouseId());
item.setOldWarehouseCode(asset.getWarehouseCode());
item.setOldWarehouseName(asset.getWarehouseName());
item.setOldLocationId(asset.getLocationId());
item.setOldLocationCode(asset.getLocationCode());
item.setOldLocationName(asset.getLocationName());
}
/**
*
* <p>++使使
* 使使</p>
*/
private void fillTargetSnapshots(AmsTransferOrderItem item, AmsAsset asset)
{
AmsWarehouse warehouse = requireEnabledWarehouse(item.getNewWarehouseId());
AmsAssetLocation location = requireEnabledLocation(item.getNewLocationId());
if (!warehouse.getWarehouseId().equals(location.getWarehouseId()))
{
throw new ServiceException(StringUtils.format("资产【{}】的新位置不属于所选新仓库", asset.getAssetCode()));
}
item.setNewWarehouseCode(warehouse.getWarehouseCode());
item.setNewWarehouseName(warehouse.getWarehouseName());
item.setNewLocationCode(location.getLocationCode());
item.setNewLocationName(location.getLocationName());
if (StringUtils.equals(AssetStatus.IN_USE, asset.getAssetStatus()))
{
SysDept dept = requireNormalDept(item.getNewDeptId());
SysUser user = requireNormalUser(item.getNewUserId());
if (!Objects.equals(user.getDeptId(), dept.getDeptId()))
{
throw new ServiceException(StringUtils.format("资产【{}】的新使用人不属于所选新部门", asset.getAssetCode()));
}
item.setNewDeptName(dept.getDeptName());
item.setNewUserName(user.getUserName());
}
else
{
// 在库资产没有使用归属,前端即使伪造部门或人员也必须清空。
item.setNewDeptId(null);
item.setNewDeptName(null);
item.setNewUserId(null);
item.setNewUserName(null);
}
}
/** 陈旧归属校验:若草稿保存后资产台账的归属已被其他业务修改,阻断当前流转。 */
private void validateOldSnapshot(AmsTransferOrderItem item, AmsAsset asset)
{
if (!Objects.equals(item.getOldWarehouseId(), asset.getWarehouseId())
|| !Objects.equals(item.getOldLocationId(), asset.getLocationId())
|| !Objects.equals(item.getOldDeptId(), asset.getUseDeptId())
|| !Objects.equals(item.getOldUserId(), asset.getUseUserId()))
{
throw new ServiceException(StringUtils.format("资产【{}】当前归属已变化,请重新编辑并提交调拨单",
asset.getAssetCode()));
}
}
/** 调拨前后归属不能完全相同,否则调拨没有实际意义。 */
private void validateActualChange(AmsTransferOrderItem item, AmsAsset asset)
{
boolean changed = !Objects.equals(asset.getWarehouseId(), item.getNewWarehouseId())
|| !Objects.equals(asset.getLocationId(), item.getNewLocationId())
|| !Objects.equals(asset.getUseDeptId(), item.getNewDeptId())
|| !Objects.equals(asset.getUseUserId(), item.getNewUserId());
if (!changed)
{
throw new ServiceException(StringUtils.format("资产【{}】的调拨前后归属不能完全相同", asset.getAssetCode()));
}
}
private AmsWarehouse requireEnabledWarehouse(Long warehouseId)
{
if (StringUtils.isNull(warehouseId))
{
throw new ServiceException("新仓库不能为空");
}
AmsWarehouse warehouse = amsWarehouseService.selectAmsWarehouseByWarehouseId(warehouseId);
if (StringUtils.isNull(warehouse) || !StringUtils.equals(ENABLED_YES, warehouse.getEnabled()))
{
throw new ServiceException("新仓库不存在或已停用");
}
return warehouse;
}
private AmsAssetLocation requireEnabledLocation(Long locationId)
{
if (StringUtils.isNull(locationId))
{
throw new ServiceException("新位置不能为空");
}
AmsAssetLocation location = amsAssetLocationService.selectAmsAssetLocationByLocationId(locationId);
if (StringUtils.isNull(location) || !StringUtils.equals(ENABLED_YES, location.getEnabled()))
{
throw new ServiceException("新位置不存在或已停用");
}
return location;
}
private SysDept requireNormalDept(Long deptId)
{
if (StringUtils.isNull(deptId))
{
throw new ServiceException("在用资产的新使用部门不能为空");
}
SysDept dept = sysDeptService.selectDeptById(deptId);
if (StringUtils.isNull(dept)
|| !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getStatus())
|| !StringUtils.equals(DEL_FLAG_NORMAL, dept.getDelFlag()))
{
throw new ServiceException("新使用部门不存在或已停用");
}
return dept;
}
private SysUser requireNormalUser(Long userId)
{
if (StringUtils.isNull(userId))
{
throw new ServiceException("在用资产的新使用人不能为空");
}
SysUser user = sysUserService.selectUserById(userId);
if (StringUtils.isNull(user)
|| !StringUtils.equals(UserConstants.NORMAL, user.getStatus())
|| !StringUtils.equals(DEL_FLAG_NORMAL, user.getDelFlag()))
{
throw new ServiceException("新使用人不存在或已停用");
}
return user;
}
private AmsTransferOrder requireOrderForUpdate(Long orderId, String requiredStatus, String message)
{
if (StringUtils.isNull(orderId))
{
throw new ServiceException("调拨单ID不能为空");
}
AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
if (StringUtils.isNull(order))
{
throw new ServiceException("调拨单不存在或已删除");
}
if (!StringUtils.equals(requiredStatus, order.getOrderStatus()))
{
throw new ServiceException(message);
}
return order;
}
private void requireDraft(AmsTransferOrder order)
{
if (!StringUtils.equals(TransferOrderStatus.DRAFT, order.getOrderStatus()))
{
throw new ServiceException("仅草稿调拨单允许删除");
}
}
/** 按 assetId 升序排列明细行,确保与其他业务使用相同锁序消除死锁。 */
private List<AmsTransferOrderItem> sortedItems(AmsTransferOrder order)
{
List<AmsTransferOrderItem> items = order.getAmsTransferOrderItemList();
if (items == null)
{
return new ArrayList<>();
}
List<AmsTransferOrderItem> sortedItems = new ArrayList<>(items);
sortedItems.sort(Comparator.comparing(AmsTransferOrderItem::getAssetId,
Comparator.nullsFirst(Long::compareTo)));
return sortedItems;
}
private void insertTransferOrderItems(AmsTransferOrder order)
{
List<AmsTransferOrderItem> list = new ArrayList<>();
Date now = DateUtils.getNowDate();
for (AmsTransferOrderItem item : order.getAmsTransferOrderItemList())
{
item.setItemId(null);
item.setOrderId(order.getOrderId());
item.setCreateBy(StringUtils.isNotEmpty(order.getCreateBy()) ? order.getCreateBy() : order.getUpdateBy());
item.setCreateTime(now);
item.setUpdateBy(null);
item.setUpdateTime(null);
list.add(item);
}
if (amsTransferOrderMapper.batchAmsTransferOrderItem(list) != list.size())
{
throw new ServiceException("调拨单明细保存失败");
}
}
private void validateOperator(Long userId, String userName, String fieldName)
{
if (StringUtils.isNull(userId) || StringUtils.isEmpty(StringUtils.trim(userName)))
{
throw new ServiceException(fieldName + "ID和名称不能为空");
}
validateLength(StringUtils.trim(userName), 100, fieldName + "名称");
}
private void validateLoginName(String loginName)
{
if (StringUtils.isEmpty(StringUtils.trim(loginName)))
{
throw new ServiceException("操作登录账号不能为空");
}
validateLength(StringUtils.trim(loginName), 64, "操作登录账号");
}
private void validateLength(String value, int maxLength, String fieldName)
{
if (StringUtils.isNotEmpty(value) && value.length() > maxLength)
{
throw new ServiceException(fieldName + "长度不能超过" + maxLength + "个字符");
}
}
}

@ -3,183 +3,328 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.asset.mapper.AmsTransferOrderMapper">
<resultMap type="AmsTransferOrder" id="AmsTransferOrderResult">
<result property="orderId" column="order_id" />
<result property="transferNo" column="transfer_no" />
<result property="applicantId" column="applicant_id" />
<result property="applicantName" column="applicant_name" />
<result property="transferReason" column="transfer_reason" />
<result property="confirmUserId" column="confirm_user_id" />
<result property="confirmUserName" column="confirm_user_name" />
<result property="confirmTime" column="confirm_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" />
<result property="orderId" column="order_id" />
<result property="transferNo" column="transfer_no" />
<result property="applicantId" column="applicant_id" />
<result property="applicantName" column="applicant_name" />
<result property="transferReason" column="transfer_reason" />
<result property="confirmUserId" column="confirm_user_id" />
<result property="confirmUserName" column="confirm_user_name" />
<result property="confirmTime" column="confirm_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="AmsTransferOrderAmsTransferOrderItemResult" type="AmsTransferOrder" extends="AmsTransferOrderResult">
<collection property="amsTransferOrderItemList" ofType="AmsTransferOrderItem" column="order_id" select="selectAmsTransferOrderItemList" />
<resultMap id="AmsTransferOrderItemCollectionResult" type="AmsTransferOrder" extends="AmsTransferOrderResult">
<collection property="amsTransferOrderItemList" ofType="AmsTransferOrderItem"
column="order_id" select="selectAmsTransferOrderItemList" />
</resultMap>
<resultMap type="AmsTransferOrderItem" id="AmsTransferOrderItemResult">
<result property="itemId" column="item_id" />
<result property="orderId" column="order_id" />
<result property="transferNo" column="transfer_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="oldDeptId" column="old_dept_id" />
<result property="oldDeptName" column="old_dept_name" />
<result property="newDeptId" column="new_dept_id" />
<result property="newDeptName" column="new_dept_name" />
<result property="oldUserId" column="old_user_id" />
<result property="oldUserName" column="old_user_name" />
<result property="newUserId" column="new_user_id" />
<result property="newUserName" column="new_user_name" />
<result property="oldWarehouseId" column="old_warehouse_id" />
<result property="oldWarehouseCode" column="old_warehouse_code" />
<result property="oldWarehouseName" column="old_warehouse_name" />
<result property="newWarehouseId" column="new_warehouse_id" />
<result property="newWarehouseCode" column="new_warehouse_code" />
<result property="newWarehouseName" column="new_warehouse_name" />
<result property="oldLocationId" column="old_location_id" />
<result property="oldLocationCode" column="old_location_code" />
<result property="oldLocationName" column="old_location_name" />
<result property="newLocationId" column="new_location_id" />
<result property="newLocationCode" column="new_location_code" />
<result property="newLocationName" column="new_location_name" />
<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" />
<result property="itemId" column="item_id" />
<result property="orderId" column="order_id" />
<result property="transferNo" column="transfer_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="assetStatus" column="asset_status" />
<result property="oldDeptId" column="old_dept_id" />
<result property="oldDeptName" column="old_dept_name" />
<result property="newDeptId" column="new_dept_id" />
<result property="newDeptName" column="new_dept_name" />
<result property="oldUserId" column="old_user_id" />
<result property="oldUserName" column="old_user_name" />
<result property="newUserId" column="new_user_id" />
<result property="newUserName" column="new_user_name" />
<result property="oldWarehouseId" column="old_warehouse_id" />
<result property="oldWarehouseCode" column="old_warehouse_code" />
<result property="oldWarehouseName" column="old_warehouse_name" />
<result property="newWarehouseId" column="new_warehouse_id" />
<result property="newWarehouseCode" column="new_warehouse_code" />
<result property="newWarehouseName" column="new_warehouse_name" />
<result property="oldLocationId" column="old_location_id" />
<result property="oldLocationCode" column="old_location_code" />
<result property="oldLocationName" column="old_location_name" />
<result property="newLocationId" column="new_location_id" />
<result property="newLocationCode" column="new_location_code" />
<result property="newLocationName" column="new_location_name" />
<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 type="AmsAsset" id="AvailableTransferAssetResult">
<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="assetStatus" column="asset_status" />
<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="useDeptId" column="use_dept_id" />
<result property="useDeptName" column="use_dept_name" />
<result property="useUserId" column="use_user_id" />
<result property="useUserName" column="use_user_name" />
<result property="tagCode" column="tag_code" />
</resultMap>
<sql id="selectAmsTransferOrderVo">
select order_id, transfer_no, applicant_id, applicant_name, transfer_reason, confirm_user_id, confirm_user_name, confirm_time, order_status, create_by, create_time, update_by, update_time, remark, del_flag from ams_transfer_order
select order_id, transfer_no, applicant_id, applicant_name, transfer_reason,
confirm_user_id, confirm_user_name, confirm_time, order_status,
create_by, create_time, update_by, update_time, remark, del_flag
from ams_transfer_order
</sql>
<select id="selectAmsTransferOrderList" parameterType="AmsTransferOrder" resultMap="AmsTransferOrderResult">
<include refid="selectAmsTransferOrderVo"/>
<where>
<if test="transferNo != null and transferNo != ''"> and transfer_no = #{transferNo}</if>
<if test="applicantId != null "> and applicant_id = #{applicantId}</if>
<if test="applicantName != null and applicantName != ''"> and applicant_name like concat('%', #{applicantName}, '%')</if>
<if test="transferReason != null and transferReason != ''"> and transfer_reason = #{transferReason}</if>
<if test="confirmUserId != null "> and confirm_user_id = #{confirmUserId}</if>
<if test="confirmUserName != null and confirmUserName != ''"> and confirm_user_name like concat('%', #{confirmUserName}, '%')</if>
<if test="confirmTime != null "> and confirm_time = #{confirmTime}</if>
<if test="orderStatus != null and orderStatus != ''"> and order_status = #{orderStatus}</if>
<where>
del_flag = '0'
<if test="transferNo != null and transferNo != ''">
and transfer_no like concat(#{transferNo}, '%')
</if>
<if test="applicantName != null and applicantName != ''">
and applicant_name like concat('%', #{applicantName}, '%')
</if>
<if test="params.beginConfirmTime != null and params.beginConfirmTime != ''">
and confirm_time &gt;= #{params.beginConfirmTime}
</if>
<if test="params.endConfirmTime != null and params.endConfirmTime != ''">
and confirm_time &lt; date_add(#{params.endConfirmTime}, interval 1 day)
</if>
<if test="orderStatus != null and orderStatus != ''">
and order_status = #{orderStatus}
</if>
<if test="params.assetCode != null and params.assetCode != ''">
and exists (
select 1 from ams_transfer_order_item item
where item.order_id = ams_transfer_order.order_id
and item.del_flag = '0'
and item.asset_code like concat(#{params.assetCode}, '%')
)
</if>
<if test="params.oldWarehouseId != null and params.oldWarehouseId != ''">
and exists (
select 1 from ams_transfer_order_item item
where item.order_id = ams_transfer_order.order_id
and item.del_flag = '0'
and item.old_warehouse_id = #{params.oldWarehouseId}
)
</if>
<if test="params.newWarehouseId != null and params.newWarehouseId != ''">
and exists (
select 1 from ams_transfer_order_item item
where item.order_id = ams_transfer_order.order_id
and item.del_flag = '0'
and item.new_warehouse_id = #{params.newWarehouseId}
)
</if>
</where>
</select>
<select id="selectAmsTransferOrderByOrderId" parameterType="Long" resultMap="AmsTransferOrderAmsTransferOrderItemResult">
select order_id, transfer_no, applicant_id, applicant_name, transfer_reason, confirm_user_id, confirm_user_name, confirm_time, order_status, create_by, create_time, update_by, update_time, remark, del_flag
from ams_transfer_order
where order_id = #{orderId}
order by create_time desc, order_id desc
</select>
<select id="selectAmsTransferOrderByOrderId" parameterType="Long"
resultMap="AmsTransferOrderItemCollectionResult">
<include refid="selectAmsTransferOrderVo"/>
where order_id = #{orderId} and del_flag = '0'
</select>
<select id="selectAmsTransferOrderByOrderIdForUpdate" parameterType="Long"
resultMap="AmsTransferOrderItemCollectionResult">
<include refid="selectAmsTransferOrderVo"/>
where order_id = #{orderId} and del_flag = '0'
for update
</select>
<!-- 查询可调拨资产候选列表:筛选在库/在用状态,通过 NOT EXISTS 排除已被其他有效调拨单占用的资产 -->
<select id="selectAvailableTransferAssetList" resultMap="AvailableTransferAssetResult">
select asset.asset_id, asset.asset_code, asset.asset_name, asset.category_id,
asset.category_code, asset.category_name, asset.spec_model, asset.brand,
asset.asset_status, asset.warehouse_id, asset.warehouse_code, asset.warehouse_name,
asset.location_id, asset.location_code, asset.location_name, asset.use_dept_id,
asset.use_dept_name, asset.use_user_id, asset.use_user_name, asset.tag_code
from ams_asset asset
where asset.del_flag = '0'
and asset.asset_status in (#{stockStatus}, #{inUseStatus})
and not exists (
select 1
from ams_transfer_order_item item
inner join ams_transfer_order transfer_order on transfer_order.order_id = item.order_id
and transfer_order.del_flag = '0'
and transfer_order.order_status in (#{draftStatus}, #{pendingStatus})
where item.asset_id = asset.asset_id
and item.del_flag = '0'
<if test="currentOrderId != null">
and transfer_order.order_id != #{currentOrderId}
</if>
)
<if test="asset.assetCode != null and asset.assetCode != ''">
and asset.asset_code like concat('%', #{asset.assetCode}, '%')
</if>
<if test="asset.assetName != null and asset.assetName != ''">
and asset.asset_name like concat('%', #{asset.assetName}, '%')
</if>
<if test="asset.categoryName != null and asset.categoryName != ''">
and asset.category_name like concat('%', #{asset.categoryName}, '%')
</if>
<if test="asset.assetStatus != null and asset.assetStatus != ''">
and asset.asset_status = #{asset.assetStatus}
</if>
order by asset.asset_id
</select>
<!-- 保存草稿时防重复占用校验:统计同一资产在其他有效调拨单(草稿+待确认)中的引用次数 -->
<select id="countOtherActiveTransferOrderByAssetId" resultType="int">
select count(1)
from ams_transfer_order_item item
inner join ams_transfer_order transfer_order on transfer_order.order_id = item.order_id
and transfer_order.del_flag = '0'
and transfer_order.order_status in (#{draftStatus}, #{pendingStatus})
where item.asset_id = #{assetId}
and item.del_flag = '0'
<if test="currentOrderId != null">
and transfer_order.order_id != #{currentOrderId}
</if>
</select>
<!-- 查询调拨单明细LEFT JOIN ams_asset 实时取资产当前状态而非冒余存储 -->
<select id="selectAmsTransferOrderItemList" resultMap="AmsTransferOrderItemResult">
select item_id, order_id, transfer_no, asset_id, asset_code, asset_name, category_id, category_code, category_name, spec_model, brand, old_dept_id, old_dept_name, new_dept_id, new_dept_name, old_user_id, old_user_name, new_user_id, new_user_name, old_warehouse_id, old_warehouse_code, old_warehouse_name, new_warehouse_id, new_warehouse_code, new_warehouse_name, old_location_id, old_location_code, old_location_name, new_location_id, new_location_code, new_location_name, create_by, create_time, update_by, update_time, remark, del_flag
from ams_transfer_order_item
where order_id = #{order_id}
select item.item_id, item.order_id, item.transfer_no, item.asset_id, item.asset_code,
item.asset_name, item.category_id, item.category_code, item.category_name,
item.spec_model, item.brand, asset.asset_status, item.old_dept_id,
item.old_dept_name, item.new_dept_id, item.new_dept_name, item.old_user_id,
item.old_user_name, item.new_user_id, item.new_user_name, item.old_warehouse_id,
item.old_warehouse_code, item.old_warehouse_name, item.new_warehouse_id,
item.new_warehouse_code, item.new_warehouse_name, item.old_location_id,
item.old_location_code, item.old_location_name, item.new_location_id,
item.new_location_code, item.new_location_name, item.create_by, item.create_time,
item.update_by, item.update_time, item.remark, item.del_flag
from ams_transfer_order_item item
left join ams_asset asset on asset.asset_id = item.asset_id and asset.del_flag = '0'
where item.order_id = #{order_id} and item.del_flag = '0'
order by item.item_id
</select>
<insert id="insertAmsTransferOrder" parameterType="AmsTransferOrder" useGeneratedKeys="true" keyProperty="orderId">
insert into ams_transfer_order
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="transferNo != null and transferNo != ''">transfer_no,</if>
<if test="applicantId != null">applicant_id,</if>
<if test="applicantName != null">applicant_name,</if>
<if test="transferReason != null">transfer_reason,</if>
<if test="confirmUserId != null">confirm_user_id,</if>
<if test="confirmUserName != null">confirm_user_name,</if>
<if test="confirmTime != null">confirm_time,</if>
<if test="orderStatus != null and orderStatus != ''">order_status,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="delFlag != null and delFlag != ''">del_flag,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="transferNo != null and transferNo != ''">#{transferNo},</if>
<if test="applicantId != null">#{applicantId},</if>
<if test="applicantName != null">#{applicantName},</if>
<if test="transferReason != null">#{transferReason},</if>
<if test="confirmUserId != null">#{confirmUserId},</if>
<if test="confirmUserName != null">#{confirmUserName},</if>
<if test="confirmTime != null">#{confirmTime},</if>
<if test="orderStatus != null and orderStatus != ''">#{orderStatus},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
</trim>
<insert id="insertAmsTransferOrder" parameterType="AmsTransferOrder"
useGeneratedKeys="true" keyProperty="orderId">
insert into ams_transfer_order (
transfer_no, applicant_id, applicant_name, transfer_reason, order_status,
create_by, create_time, remark, del_flag
) values (
#{transferNo}, #{applicantId}, #{applicantName}, #{transferReason}, #{orderStatus},
#{createBy}, #{createTime}, #{remark}, #{delFlag}
)
</insert>
<update id="updateAmsTransferOrder" parameterType="AmsTransferOrder">
update ams_transfer_order
<trim prefix="SET" suffixOverrides=",">
<if test="transferNo != null and transferNo != ''">transfer_no = #{transferNo},</if>
<if test="applicantId != null">applicant_id = #{applicantId},</if>
<if test="applicantName != null">applicant_name = #{applicantName},</if>
<if test="transferReason != null">transfer_reason = #{transferReason},</if>
<if test="confirmUserId != null">confirm_user_id = #{confirmUserId},</if>
<if test="confirmUserName != null">confirm_user_name = #{confirmUserName},</if>
<if test="confirmTime != null">confirm_time = #{confirmTime},</if>
<if test="orderStatus != null and orderStatus != ''">order_status = #{orderStatus},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
</trim>
where order_id = #{orderId}
set transfer_reason = #{transferReason},
update_by = #{updateBy},
update_time = #{updateTime},
remark = #{remark}
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
</update>
<delete id="deleteAmsTransferOrderByOrderId" parameterType="Long">
delete from ams_transfer_order where order_id = #{orderId}
</delete>
<!-- 提交调拨单DRAFT → PENDING_CONFIRM通过 WHERE order_status='DRAFT' 保证幂等性 -->
<update id="submitAmsTransferOrder" parameterType="AmsTransferOrder">
update ams_transfer_order
set order_status = #{orderStatus},
update_by = #{updateBy},
update_time = #{updateTime}
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
</update>
<delete id="deleteAmsTransferOrderByOrderIds" parameterType="String">
delete from ams_transfer_order where order_id in
<!-- 确认调拨单PENDING_CONFIRM → TRANSFERRED写入确认人信息 -->
<update id="confirmAmsTransferOrder" parameterType="AmsTransferOrder">
update ams_transfer_order
set order_status = #{orderStatus},
confirm_user_id = #{confirmUserId},
confirm_user_name = #{confirmUserName},
confirm_time = #{confirmTime},
update_by = #{updateBy},
update_time = #{updateTime}
where order_id = #{orderId} and del_flag = '0' and order_status = 'PENDING_CONFIRM'
</update>
<update id="deleteAmsTransferOrderByOrderId" parameterType="Long">
update ams_transfer_order
set del_flag = '1'
where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
</update>
<update id="deleteAmsTransferOrderByOrderIds" parameterType="String">
update ams_transfer_order
set del_flag = '1'
where order_id in
<foreach item="orderId" collection="array" open="(" separator="," close=")">
#{orderId}
</foreach>
</delete>
<delete id="deleteAmsTransferOrderItemByOrderIds" parameterType="String">
delete from ams_transfer_order_item where order_id in
and del_flag = '0' and order_status = 'DRAFT'
</update>
<update id="deleteAmsTransferOrderItemByOrderIds" parameterType="String">
update ams_transfer_order_item
set del_flag = '1'
where order_id in
<foreach item="orderId" collection="array" open="(" separator="," close=")">
#{orderId}
</foreach>
</delete>
and del_flag = '0'
</update>
<delete id="deleteAmsTransferOrderItemByOrderId" parameterType="Long">
delete from ams_transfer_order_item where order_id = #{orderId}
</delete>
<update id="deleteAmsTransferOrderItemByOrderId" parameterType="Long">
update ams_transfer_order_item
set del_flag = '1'
where order_id = #{orderId} and del_flag = '0'
</update>
<insert id="batchAmsTransferOrderItem">
insert into ams_transfer_order_item( item_id, order_id, transfer_no, asset_id, asset_code, asset_name, category_id, category_code, category_name, spec_model, brand, old_dept_id, old_dept_name, new_dept_id, new_dept_name, old_user_id, old_user_name, new_user_id, new_user_name, old_warehouse_id, old_warehouse_code, old_warehouse_name, new_warehouse_id, new_warehouse_code, new_warehouse_name, old_location_id, old_location_code, old_location_name, new_location_id, new_location_code, new_location_name, create_by, create_time, update_by, update_time, remark, del_flag) values
<foreach item="item" index="index" collection="list" separator=",">
( #{item.itemId}, #{item.orderId}, #{item.transferNo}, #{item.assetId}, #{item.assetCode}, #{item.assetName}, #{item.categoryId}, #{item.categoryCode}, #{item.categoryName}, #{item.specModel}, #{item.brand}, #{item.oldDeptId}, #{item.oldDeptName}, #{item.newDeptId}, #{item.newDeptName}, #{item.oldUserId}, #{item.oldUserName}, #{item.newUserId}, #{item.newUserName}, #{item.oldWarehouseId}, #{item.oldWarehouseCode}, #{item.oldWarehouseName}, #{item.newWarehouseId}, #{item.newWarehouseCode}, #{item.newWarehouseName}, #{item.oldLocationId}, #{item.oldLocationCode}, #{item.oldLocationName}, #{item.newLocationId}, #{item.newLocationCode}, #{item.newLocationName}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.remark}, #{item.delFlag})
insert into ams_transfer_order_item (
order_id, transfer_no, asset_id, asset_code, asset_name, category_id, category_code,
category_name, spec_model, brand, old_dept_id, old_dept_name, new_dept_id,
new_dept_name, old_user_id, old_user_name, new_user_id, new_user_name,
old_warehouse_id, old_warehouse_code, old_warehouse_name, new_warehouse_id,
new_warehouse_code, new_warehouse_name, old_location_id, old_location_code,
old_location_name, new_location_id, new_location_code, new_location_name,
create_by, create_time, remark, del_flag
) values
<foreach item="item" collection="list" separator=",">
(
#{item.orderId}, #{item.transferNo}, #{item.assetId}, #{item.assetCode},
#{item.assetName}, #{item.categoryId}, #{item.categoryCode}, #{item.categoryName},
#{item.specModel}, #{item.brand}, #{item.oldDeptId}, #{item.oldDeptName},
#{item.newDeptId}, #{item.newDeptName}, #{item.oldUserId}, #{item.oldUserName},
#{item.newUserId}, #{item.newUserName}, #{item.oldWarehouseId},
#{item.oldWarehouseCode}, #{item.oldWarehouseName}, #{item.newWarehouseId},
#{item.newWarehouseCode}, #{item.newWarehouseName}, #{item.oldLocationId},
#{item.oldLocationCode}, #{item.oldLocationName}, #{item.newLocationId},
#{item.newLocationCode}, #{item.newLocationName}, #{item.createBy},
#{item.createTime}, #{item.remark}, #{item.delFlag}
)
</foreach>
</insert>
</mapper>
</mapper>

@ -1,103 +1,46 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('新增调拨管理')" />
<th:block th:include="include :: datetimepicker-css" />
<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-transfer-add">
<h4 class="form-header h4">调拨管理信息</h4>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">调拨单号:</label>
<div class="col-sm-8">
<input name="transferNo" class="form-control" type="text" required>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人ID</label>
<div class="col-sm-8">
<input name="applicantId" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人名称快照:</label>
<div class="col-sm-8">
<input name="applicantName" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">调拨原因:</label>
<div class="col-sm-8">
<textarea name="transferReason" class="form-control"></textarea>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认人ID</label>
<div class="col-sm-8">
<input name="confirmUserId" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认人名称快照:</label>
<div class="col-sm-8">
<input name="confirmUserName" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认时间:</label>
<div class="col-sm-8">
<div class="input-group date">
<input name="confirmTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<h4 class="form-header h4">基本信息</h4>
<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="transferReason" maxlength="500" class="form-control" rows="3"></textarea>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">单据状态:</label>
<div class="col-sm-8">
<select name="orderStatus" class="form-control" th:with="type=${@dict.getType('ams_transfer_status')}" required>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
</select>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">备注:</label>
<div class="col-sm-8">
<textarea name="remark" class="form-control"></textarea>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">删除标志0存在1删除</label>
<div class="col-sm-8">
<input name="delFlag" class="form-control" type="text" required>
</div>
</div>
</div>
<h4 class="form-header h4">调拨单明细信息</h4>
<div class="row">
<div class="col-xs-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">
<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"></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="selectAssets()">
<i class="fa fa-plus"> 选择资产</i>
</button>
<button type="button" class="btn btn-white btn-sm" onclick="removeSelectedAssets()">
<i class="fa fa-minus"> 删除</i>
</button>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
@ -106,27 +49,34 @@
</form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: datetimepicker-js" />
<script th:inline="javascript">
var prefix = ctx + "asset/transfer"
$("#form-transfer-add").validate({
focusCleanup: true
});
var prefix = ctx + "asset/transfer";
var warehouseList = [[${warehouseList}]];
var locationList = [[${locationList}]];
var deptList = [[${deptList}]];
var userList = [[${userList}]];
var assetStatusDatas = [[${@dict.getType('ams_asset_status')}]];
$("#form-transfer-add").validate({ focusCleanup: true });
/* 提交前校验明细不能为空 */
function submitHandler() {
if ($("#bootstrap-table").bootstrapTable("getData").length === 0) {
$.modal.alertWarning("请至少添加一条调拨明细");
return;
}
if ($.validate.form()) {
$.operate.save(prefix + "/add", $('#form-transfer-add').serialize());
$.operate.save(prefix + "/add", $("#form-transfer-add").serialize());
}
}
$("input[name='confirmTime']").datetimepicker({
format: "yyyy-mm-dd",
minView: "month",
autoclose: true
$(function() {
initDetailTable([]);
});
$(function() {
var options = {
function initDetailTable(data) {
$.table.init({
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
@ -137,383 +87,298 @@
checkbox: true
},
{
field: 'index',
align: 'center',
title: "序号",
formatter: function (value, row, index) {
var columnIndex = $.common.sprintf("<input type='hidden' name='index' value='%s'>", $.table.serialNumber(index));
return columnIndex + $.table.serialNumber(index);
field: "assetId",
title: "资产",
formatter: function(value, row, index) {
return buildAssetCell(row, index);
}
},
{
field: 'transferNo',
align: 'center',
title: '调拨单号快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].transferNo' value='%s'>", index, value);
return html;
field: "assetStatus",
title: "状态",
formatter: function(value) {
return $.table.selectDictLabel(assetStatusDatas, value);
}
},
{
field: 'assetId',
align: 'center',
title: '资产ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetId' value='%s'>", index, value);
return html;
field: "oldWarehouseName",
title: "当前归属",
formatter: function(value, row) {
return buildCurrentOwnership(row);
}
},
{
field: 'assetCode',
align: 'center',
title: '资产编码快照',
field: "newWarehouseId",
title: "新仓库",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetCode' value='%s'>", index, value);
return html;
return buildWarehouseSelect(value, index);
}
},
{
field: 'assetName',
align: 'center',
title: '资产名称快照',
field: "newLocationId",
title: "新位置",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetName' value='%s'>", index, value);
return html;
return buildLocationSelect(value, row, index);
}
},
{
field: 'categoryId',
align: 'center',
title: '资产类别ID快照',
field: "newDeptId",
title: "新部门",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryId' value='%s'>", index, value);
return html;
return buildDeptSelect(value, row, index);
}
},
{
field: 'categoryCode',
align: 'center',
title: '类别编码快照',
field: "newUserId",
title: "新使用人",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryCode' value='%s'>", index, value);
return html;
return buildUserSelect(value, row, index);
}
},
{
field: 'categoryName',
align: 'center',
title: '类别名称快照',
field: "remark",
title: "明细备注",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryName' value='%s'>", index, value);
return html;
return buildInput("amsTransferOrderItemList[" + index + "].remark", value, 500);
}
},
{
field: 'specModel',
align: 'center',
title: '规格型号快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].specModel' value='%s'>", index, value);
return html;
}
},
{
field: 'brand',
align: 'center',
title: '品牌快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].brand' value='%s'>", index, value);
return html;
}
},
{
field: 'oldDeptId',
align: 'center',
title: '原部门ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldDeptId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldDeptName',
align: 'center',
title: '原部门名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldDeptName' value='%s'>", index, value);
return html;
}
},
{
field: 'newDeptId',
align: 'center',
title: '新部门ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newDeptId' value='%s'>", index, value);
return html;
}
},
{
field: 'newDeptName',
align: 'center',
title: '新部门名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newDeptName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldUserId',
align: 'center',
title: '原使用人ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldUserId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldUserName',
align: 'center',
title: '原使用人名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldUserName' value='%s'>", index, value);
return html;
}
},
{
field: 'newUserId',
align: 'center',
title: '新使用人ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newUserId' value='%s'>", index, value);
return html;
}
},
{
field: 'newUserName',
align: 'center',
title: '新使用人名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newUserName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseId',
align: 'center',
title: '原仓库ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseCode',
align: 'center',
title: '原仓库编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseCode' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseName',
align: 'center',
title: '原仓库名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseName' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseId',
align: 'center',
title: '新仓库ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseId' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseCode',
align: 'center',
title: '新仓库编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseCode' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseName',
align: 'center',
title: '新仓库名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationId',
align: 'center',
title: '原位置ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationCode',
align: 'center',
title: '原位置编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationCode' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationName',
align: 'center',
title: '原位置名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationName' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationId',
align: 'center',
title: '新位置ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationId' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationCode',
align: 'center',
title: '新位置编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationCode' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationName',
align: 'center',
title: '新位置名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationName' value='%s'>", index, value);
return html;
}
},
{
field: 'createBy',
align: 'center',
title: '创建者',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].createBy' value='%s'>", index, value);
return html;
}
},
{
field: 'createTime',
align: 'center',
title: '创建时间',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].createTime' value='%s'>", index, value);
return html;
}
},
{
field: 'updateBy',
align: 'center',
title: '更新者',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].updateBy' value='%s'>", index, value);
return html;
}
},
{
field: 'updateTime',
align: 'center',
title: '更新时间',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].updateTime' value='%s'>", index, value);
return html;
}
},
{
field: 'remark',
align: 'center',
title: '备注',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].remark' value='%s'>", index, value);
return html;
}
},
{
field: 'delFlag',
align: 'center',
title: '删除标志0存在1删除',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].delFlag' value='%s'>", index, value);
return html;
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var value = $.common.isNotEmpty(row.index) ? row.index : $.table.serialNumber(index);
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="sub.delRowByIndex(\'' + value + '\')"><i class="fa fa-remove"></i>删除</a>';
title: "操作",
align: "center",
formatter: function(value, row) {
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="removeAsset(\''
+ row.assetId + '\')"><i class="fa fa-remove"></i>删除</a>';
}
}]
};
$.table.init(options);
});
});
}
function addRow() {
var count = $("#" + table.options.id).bootstrapTable('getData').length;
var row = {
index: $.table.serialNumber(count),
transferNo: "",
assetId: "",
assetCode: "",
assetName: "",
categoryId: "",
categoryCode: "",
categoryName: "",
specModel: "",
brand: "",
oldDeptId: "",
oldDeptName: "",
newDeptId: "",
newDeptName: "",
oldUserId: "",
oldUserName: "",
newUserId: "",
newUserName: "",
oldWarehouseId: "",
oldWarehouseCode: "",
oldWarehouseName: "",
newWarehouseId: "",
newWarehouseCode: "",
newWarehouseName: "",
oldLocationId: "",
oldLocationCode: "",
oldLocationName: "",
newLocationId: "",
newLocationCode: "",
newLocationName: "",
createBy: "",
createTime: "",
updateBy: "",
updateTime: "",
remark: "",
delFlag: "",
/* 弹窗打开可调拨资产选择器(新增模式,不传 orderId */
function selectAssets() {
$.modal.openOptions({
title: "选择可调拨资产",
url: prefix + "/selectAsset",
width: "1200",
height: "680",
callBack: addSelectedAssets
});
}
/* 弹窗回调:从选择器取回勾选行,去重后追加到明细表 */
function addSelectedAssets(index, layero) {
var selectedAssets = layero.find("iframe")[0].contentWindow.getSelectedAssets();
if (!selectedAssets || selectedAssets.length === 0) {
$.modal.alertWarning("请至少选择一条资产记录");
return;
}
sub.addRow(row);
syncDetailRows();
var existing = {};
$.each($("#bootstrap-table").bootstrapTable("getData"), function(i, row) {
existing[String(row.assetId)] = true;
});
$.each(selectedAssets, function(i, asset) {
if (!existing[String(asset.assetId)]) {
$("#bootstrap-table").bootstrapTable("insertRow", {
index: $("#bootstrap-table").bootstrapTable("getData").length,
row: buildTransferItem(asset)
});
existing[String(asset.assetId)] = true;
}
});
$.modal.close(index);
}
/**
* 将资产台账字段映射为调拨明细行的初始值。
* 注意资产选择器返回的是台账字段名warehouseId
* 需映射为调拨明细的「原」前缀字段oldWarehouseId
*/
function buildTransferItem(asset) {
return {
assetId: asset.assetId,
assetCode: asset.assetCode,
assetName: asset.assetName,
categoryName: asset.categoryName,
assetStatus: asset.assetStatus,
oldDeptId: asset.useDeptId,
oldDeptName: asset.useDeptName,
oldUserId: asset.useUserId,
oldUserName: asset.useUserName,
oldWarehouseId: asset.warehouseId,
oldWarehouseCode: asset.warehouseCode,
oldWarehouseName: asset.warehouseName,
oldLocationId: asset.locationId,
oldLocationCode: asset.locationCode,
oldLocationName: asset.locationName,
newWarehouseId: "",
newLocationId: "",
newDeptId: "",
newUserId: "",
remark: ""
};
}
function removeSelectedAssets() {
var rows = $("#bootstrap-table").bootstrapTable("getSelections");
if (rows.length === 0) {
$.modal.alertWarning("请至少选择一条记录");
return;
}
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", {
field: "assetId",
values: $.map(rows, function(row) { return row.assetId; })
});
}
function removeAsset(assetId) {
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", { field: "assetId", values: [assetId] });
}
/* 仓库下拉变更时,清空已选位置并重新渲染行的位置下拉(仅显示属于新仓库的位置) */
function changeWarehouse(index, value) {
syncDetailRows();
var row = $("#bootstrap-table").bootstrapTable("getData")[index];
row.newWarehouseId = value;
row.newLocationId = "";
$("#bootstrap-table").bootstrapTable("updateRow", { index: index, row: row });
}
/* 部门下拉变更时,清空已选使用人并重新渲染行的使用人下拉(仅显示属于新部门的用户) */
function changeDept(index, value) {
syncDetailRows();
var row = $("#bootstrap-table").bootstrapTable("getData")[index];
row.newDeptId = value;
row.newUserId = "";
$("#bootstrap-table").bootstrapTable("updateRow", { index: index, row: row });
}
/**
* 关键技巧:在任何行操作(增、删、级联切换)前,先从 DOM 的下拉/输入元素
* 同步用户编辑值回 bootstrapTable 的数据模型,因为
* bootstrapTable getData() 不会自动跟踪嵌入在单元格内的表单元素的值变化。
*/
function syncDetailRows() {
var rows = $("#bootstrap-table").bootstrapTable("getData");
$.each(rows, function(index, row) {
var tr = $("#bootstrap-table tbody tr[data-index='" + index + "']");
row.newWarehouseId = tr.find("[name$='.newWarehouseId']").val() || "";
row.newLocationId = tr.find("[name$='.newLocationId']").val() || "";
row.newDeptId = tr.find("[name$='.newDeptId']").val() || "";
row.newUserId = tr.find("[name$='.newUserId']").val() || "";
row.remark = tr.find("[name$='.remark']").val() || "";
});
}
function buildAssetCell(row, index) {
var hidden = $("<input>").attr({
type: "hidden",
name: "amsTransferOrderItemList[" + index + "].assetId",
value: row.assetId
}).prop("outerHTML");
return hidden + $("<span>").text(row.assetCode + " - " + row.assetName).prop("outerHTML");
}
function buildCurrentOwnership(row) {
var text = (row.oldWarehouseName || "-") + " / " + (row.oldLocationName || "-");
if (row.assetStatus === "IN_USE") {
text += " / " + (row.oldDeptName || "-") + " / " + (row.oldUserName || "-");
}
return $("<span>").text(text).prop("outerHTML");
}
function buildWarehouseSelect(value, index) {
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newWarehouseId",
required: true,
onchange: "changeWarehouse(" + index + ", this.value)"
});
select.append($("<option>").val("").text("请选择新仓库"));
$.each(warehouseList, function(i, warehouse) {
var option = $("<option>").val(warehouse.warehouseId)
.text(warehouse.warehouseCode + " - " + warehouse.warehouseName);
if (String(warehouse.warehouseId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
});
return select.prop("outerHTML");
}
function buildLocationSelect(value, row, index) {
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newLocationId",
required: true
});
select.append($("<option>").val("").text("请选择新位置"));
$.each(locationList, function(i, location) {
if (String(location.warehouseId) === String(row.newWarehouseId)) {
var option = $("<option>").val(location.locationId)
.text(location.locationCode + " - " + location.locationName);
if (String(location.locationId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
}
});
return select.prop("outerHTML");
}
/* 在库资产无需设置部门,隐藏字段提交空值,后端会强制清空以防伪造 */
function buildDeptSelect(value, row, index) {
if (row.assetStatus !== "IN_USE") {
return buildHidden("amsTransferOrderItemList[" + index + "].newDeptId", "") + "在库无需设置";
}
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newDeptId",
required: true,
onchange: "changeDept(" + index + ", this.value)"
});
select.append($("<option>").val("").text("请选择新部门"));
$.each(deptList, function(i, dept) {
var option = $("<option>").val(dept.deptId).text(dept.deptName);
if (String(dept.deptId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
});
return select.prop("outerHTML");
}
/* 在库资产无需设置使用人,同部门逻辑 */
function buildUserSelect(value, row, index) {
if (row.assetStatus !== "IN_USE") {
return buildHidden("amsTransferOrderItemList[" + index + "].newUserId", "") + "在库无需设置";
}
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newUserId",
required: true
});
select.append($("<option>").val("").text("请选择新使用人"));
$.each(userList, function(i, user) {
if (String(user.deptId) === String(row.newDeptId)) {
var option = $("<option>").val(user.userId).text(user.userName + "" + user.loginName + "");
if (String(user.userId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
}
});
return select.prop("outerHTML");
}
function buildHidden(name, value) {
return $("<input>").attr({ type: "hidden", name: name, value: value }).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>
</html>

@ -1,96 +1,66 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('修改调拨管理')" />
<th:block th:include="include :: datetimepicker-css" />
<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-transfer-edit" th:object="${amsTransferOrder}">
<h4 class="form-header h4">调拨管理信息</h4>
<input name="orderId" th:field="*{orderId}" type="hidden">
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">调拨单号:</label>
<div class="col-sm-8">
<input name="transferNo" th:field="*{transferNo}" class="form-control" type="text" required>
<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="*{transferNo}" class="form-control" type="text" readonly>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人ID</label>
<div class="col-sm-8">
<input name="applicantId" th:field="*{applicantId}" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人名称快照:</label>
<div class="col-sm-8">
<input name="applicantName" th:field="*{applicantName}" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">调拨原因:</label>
<div class="col-sm-8">
<textarea name="transferReason" class="form-control">[[*{transferReason}]]</textarea>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认人ID</label>
<div class="col-sm-8">
<input name="confirmUserId" th:field="*{confirmUserId}" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认人名称快照:</label>
<div class="col-sm-8">
<input name="confirmUserName" th:field="*{confirmUserName}" class="form-control" type="text">
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认时间:</label>
<div class="col-sm-8">
<div class="input-group date">
<input name="confirmTime" th:value="${#dates.format(amsTransferOrder.confirmTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<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="*{applicantName}" class="form-control" type="text" readonly>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">单据状态:</label>
<div class="col-sm-8">
<select name="orderStatus" class="form-control" th:with="type=${@dict.getType('ams_transfer_status')}" required>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{orderStatus}"></option>
</select>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label">备注:</label>
<div class="col-sm-8">
<textarea name="remark" class="form-control">[[*{remark}]]</textarea>
</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="form-group">
<label class="col-sm-2 control-label">调拨原因:</label>
<div class="col-sm-10">
<textarea name="transferReason" maxlength="500" class="form-control"
rows="3">[[*{transferReason}]]</textarea>
</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="selectAssets()">
<i class="fa fa-plus"> 选择资产</i>
</button>
<button type="button" class="btn btn-white btn-sm" onclick="removeSelectedAssets()">
<i class="fa fa-minus"> 删除</i>
</button>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
@ -99,28 +69,36 @@
</form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: datetimepicker-js" />
<script th:inline="javascript">
var prefix = ctx + "asset/transfer";
$("#form-transfer-edit").validate({
focusCleanup: true
});
var orderId = [[${amsTransferOrder.orderId}]];
var detailList = [[${amsTransferOrder.amsTransferOrderItemList}]];
var warehouseList = [[${warehouseList}]];
var locationList = [[${locationList}]];
var deptList = [[${deptList}]];
var userList = [[${userList}]];
var assetStatusDatas = [[${@dict.getType('ams_asset_status')}]];
$("#form-transfer-edit").validate({ focusCleanup: true });
/* 提交前校验明细不能为空 */
function submitHandler() {
if ($("#bootstrap-table").bootstrapTable("getData").length === 0) {
$.modal.alertWarning("请至少保留一条调拨明细");
return;
}
if ($.validate.form()) {
$.operate.save(prefix + "/edit", $('#form-transfer-edit').serialize());
$.operate.save(prefix + "/edit", $("#form-transfer-edit").serialize());
}
}
$("input[name='confirmTime']").datetimepicker({
format: "yyyy-mm-dd",
minView: "month",
autoclose: true
$(function() {
initDetailTable(detailList);
});
$(function() {
var options = {
data: [[${amsTransferOrder.amsTransferOrderItemList}]],
function initDetailTable(data) {
$.table.init({
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
@ -131,418 +109,296 @@
checkbox: true
},
{
field: 'index',
align: 'center',
title: "序号",
formatter: function (value, row, index) {
var columnIndex = $.common.sprintf("<input type='hidden' name='index' value='%s'>", $.table.serialNumber(index));
return columnIndex + $.table.serialNumber(index);
field: "assetId",
title: "资产",
formatter: function(value, row, index) {
return buildAssetCell(row, index);
}
},
{
field: 'transferNo',
align: 'center',
title: '调拨单号快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].transferNo' value='%s'>", index, value);
return html;
field: "assetStatus",
title: "状态",
formatter: function(value) {
return $.table.selectDictLabel(assetStatusDatas, value);
}
},
{
field: 'assetId',
align: 'center',
title: '资产ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetId' value='%s'>", index, value);
return html;
field: "oldWarehouseName",
title: "当前归属",
formatter: function(value, row) {
return buildCurrentOwnership(row);
}
},
{
field: 'assetCode',
align: 'center',
title: '资产编码快照',
field: "newWarehouseId",
title: "新仓库",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetCode' value='%s'>", index, value);
return html;
return buildWarehouseSelect(value, index);
}
},
{
field: 'assetName',
align: 'center',
title: '资产名称快照',
field: "newLocationId",
title: "新位置",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].assetName' value='%s'>", index, value);
return html;
return buildLocationSelect(value, row, index);
}
},
{
field: 'categoryId',
align: 'center',
title: '资产类别ID快照',
field: "newDeptId",
title: "新部门",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryId' value='%s'>", index, value);
return html;
return buildDeptSelect(value, row, index);
}
},
{
field: 'categoryCode',
align: 'center',
title: '类别编码快照',
field: "newUserId",
title: "新使用人",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryCode' value='%s'>", index, value);
return html;
return buildUserSelect(value, row, index);
}
},
{
field: 'categoryName',
align: 'center',
title: '类别名称快照',
field: "remark",
title: "明细备注",
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].categoryName' value='%s'>", index, value);
return html;
return buildInput("amsTransferOrderItemList[" + index + "].remark", value, 500);
}
},
{
field: 'specModel',
align: 'center',
title: '规格型号快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].specModel' value='%s'>", index, value);
return html;
}
},
{
field: 'brand',
align: 'center',
title: '品牌快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].brand' value='%s'>", index, value);
return html;
}
},
{
field: 'oldDeptId',
align: 'center',
title: '原部门ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldDeptId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldDeptName',
align: 'center',
title: '原部门名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldDeptName' value='%s'>", index, value);
return html;
}
},
{
field: 'newDeptId',
align: 'center',
title: '新部门ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newDeptId' value='%s'>", index, value);
return html;
}
},
{
field: 'newDeptName',
align: 'center',
title: '新部门名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newDeptName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldUserId',
align: 'center',
title: '原使用人ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldUserId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldUserName',
align: 'center',
title: '原使用人名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldUserName' value='%s'>", index, value);
return html;
}
},
{
field: 'newUserId',
align: 'center',
title: '新使用人ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newUserId' value='%s'>", index, value);
return html;
}
},
{
field: 'newUserName',
align: 'center',
title: '新使用人名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newUserName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseId',
align: 'center',
title: '原仓库ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseCode',
align: 'center',
title: '原仓库编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseCode' value='%s'>", index, value);
return html;
}
},
{
field: 'oldWarehouseName',
align: 'center',
title: '原仓库名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldWarehouseName' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseId',
align: 'center',
title: '新仓库ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseId' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseCode',
align: 'center',
title: '新仓库编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseCode' value='%s'>", index, value);
return html;
}
},
{
field: 'newWarehouseName',
align: 'center',
title: '新仓库名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newWarehouseName' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationId',
align: 'center',
title: '原位置ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationId' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationCode',
align: 'center',
title: '原位置编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationCode' value='%s'>", index, value);
return html;
}
},
{
field: 'oldLocationName',
align: 'center',
title: '原位置名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].oldLocationName' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationId',
align: 'center',
title: '新位置ID',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationId' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationCode',
align: 'center',
title: '新位置编码快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationCode' value='%s'>", index, value);
return html;
}
},
{
field: 'newLocationName',
align: 'center',
title: '新位置名称快照',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].newLocationName' value='%s'>", index, value);
return html;
}
},
{
field: 'createBy',
align: 'center',
title: '创建者',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].createBy' value='%s'>", index, value);
return html;
}
},
{
field: 'createTime',
align: 'center',
title: '创建时间',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].createTime' value='%s'>", index, value);
return html;
}
},
{
field: 'updateBy',
align: 'center',
title: '更新者',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].updateBy' value='%s'>", index, value);
return html;
}
},
{
field: 'updateTime',
align: 'center',
title: '更新时间',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].updateTime' value='%s'>", index, value);
return html;
}
},
{
field: 'remark',
align: 'center',
title: '备注',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].remark' value='%s'>", index, value);
return html;
}
},
{
field: 'delFlag',
align: 'center',
title: '删除标志0存在1删除',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='amsTransferOrderItemList[%s].delFlag' value='%s'>", index, value);
return html;
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var value = $.common.isNotEmpty(row.index) ? row.index : $.table.serialNumber(index);
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="sub.delRowByIndex(\'' + value + '\')"><i class="fa fa-remove"></i>删除</a>';
title: "操作",
align: "center",
formatter: function(value, row) {
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="removeAsset(\''
+ row.assetId + '\')"><i class="fa fa-remove"></i>删除</a>';
}
}]
};
$.table.init(options);
});
});
}
function addRow() {
var count = $("#" + table.options.id).bootstrapTable('getData').length;
var row = {
index: $.table.serialNumber(count),
transferNo: "",
assetId: "",
assetCode: "",
assetName: "",
categoryId: "",
categoryCode: "",
categoryName: "",
specModel: "",
brand: "",
oldDeptId: "",
oldDeptName: "",
newDeptId: "",
newDeptName: "",
oldUserId: "",
oldUserName: "",
newUserId: "",
newUserName: "",
oldWarehouseId: "",
oldWarehouseCode: "",
oldWarehouseName: "",
newWarehouseId: "",
newWarehouseCode: "",
newWarehouseName: "",
oldLocationId: "",
oldLocationCode: "",
oldLocationName: "",
newLocationId: "",
newLocationCode: "",
newLocationName: "",
createBy: "",
createTime: "",
updateBy: "",
updateTime: "",
remark: "",
delFlag: "",
/* 编辑模式传入 orderId让选择器排除当前调拨单已占用的资产 */
function selectAssets() {
$.modal.openOptions({
title: "选择可调拨资产",
url: prefix + "/selectAsset?orderId=" + orderId,
width: "1200",
height: "680",
callBack: addSelectedAssets
});
}
/* 弹窗回调:从选择器取回勾选行,去重后追加到明细表 */
function addSelectedAssets(index, layero) {
var selectedAssets = layero.find("iframe")[0].contentWindow.getSelectedAssets();
if (!selectedAssets || selectedAssets.length === 0) {
$.modal.alertWarning("请至少选择一条资产记录");
return;
}
sub.addRow(row);
syncDetailRows();
var existing = {};
$.each($("#bootstrap-table").bootstrapTable("getData"), function(i, row) {
existing[String(row.assetId)] = true;
});
$.each(selectedAssets, function(i, asset) {
if (!existing[String(asset.assetId)]) {
$("#bootstrap-table").bootstrapTable("insertRow", {
index: $("#bootstrap-table").bootstrapTable("getData").length,
row: buildTransferItem(asset)
});
existing[String(asset.assetId)] = true;
}
});
$.modal.close(index);
}
/**
* 将资产台账字段映射为调拨明细行的初始值。
* 注意台账字段名warehouseId→ 调拨明细「原」前缀字段oldWarehouseId
*/
function buildTransferItem(asset) {
return {
assetId: asset.assetId,
assetCode: asset.assetCode,
assetName: asset.assetName,
categoryName: asset.categoryName,
assetStatus: asset.assetStatus,
oldDeptId: asset.useDeptId,
oldDeptName: asset.useDeptName,
oldUserId: asset.useUserId,
oldUserName: asset.useUserName,
oldWarehouseId: asset.warehouseId,
oldWarehouseCode: asset.warehouseCode,
oldWarehouseName: asset.warehouseName,
oldLocationId: asset.locationId,
oldLocationCode: asset.locationCode,
oldLocationName: asset.locationName,
newWarehouseId: "",
newLocationId: "",
newDeptId: "",
newUserId: "",
remark: ""
};
}
function removeSelectedAssets() {
var rows = $("#bootstrap-table").bootstrapTable("getSelections");
if (rows.length === 0) {
$.modal.alertWarning("请至少选择一条记录");
return;
}
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", {
field: "assetId",
values: $.map(rows, function(row) { return row.assetId; })
});
}
function removeAsset(assetId) {
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", { field: "assetId", values: [assetId] });
}
/* 仓库下拉变更时清空位置并重新渲染级联 */
function changeWarehouse(index, value) {
syncDetailRows();
var row = $("#bootstrap-table").bootstrapTable("getData")[index];
row.newWarehouseId = value;
row.newLocationId = "";
$("#bootstrap-table").bootstrapTable("updateRow", { index: index, row: row });
}
/* 部门下拉变更时清空使用人并重新渲染级联 */
function changeDept(index, value) {
syncDetailRows();
var row = $("#bootstrap-table").bootstrapTable("getData")[index];
row.newDeptId = value;
row.newUserId = "";
$("#bootstrap-table").bootstrapTable("updateRow", { index: index, row: row });
}
/**
* 关键技巧:在任何行操作前,从 DOM 同步表单元素的用户编辑值
* 回 bootstrapTable 的数据模型,避免增删行后丢失已填写数据。
*/
function syncDetailRows() {
var rows = $("#bootstrap-table").bootstrapTable("getData");
$.each(rows, function(index, row) {
var tr = $("#bootstrap-table tbody tr[data-index='" + index + "']");
row.newWarehouseId = tr.find("[name$='.newWarehouseId']").val() || "";
row.newLocationId = tr.find("[name$='.newLocationId']").val() || "";
row.newDeptId = tr.find("[name$='.newDeptId']").val() || "";
row.newUserId = tr.find("[name$='.newUserId']").val() || "";
row.remark = tr.find("[name$='.remark']").val() || "";
});
}
function buildAssetCell(row, index) {
var hidden = $("<input>").attr({
type: "hidden",
name: "amsTransferOrderItemList[" + index + "].assetId",
value: row.assetId
}).prop("outerHTML");
return hidden + $("<span>").text(row.assetCode + " - " + row.assetName).prop("outerHTML");
}
function buildCurrentOwnership(row) {
var text = (row.oldWarehouseName || "-") + " / " + (row.oldLocationName || "-");
if (row.assetStatus === "IN_USE") {
text += " / " + (row.oldDeptName || "-") + " / " + (row.oldUserName || "-");
}
return $("<span>").text(text).prop("outerHTML");
}
function buildWarehouseSelect(value, index) {
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newWarehouseId",
required: true,
onchange: "changeWarehouse(" + index + ", this.value)"
});
select.append($("<option>").val("").text("请选择新仓库"));
$.each(warehouseList, function(i, warehouse) {
var option = $("<option>").val(warehouse.warehouseId)
.text(warehouse.warehouseCode + " - " + warehouse.warehouseName);
if (String(warehouse.warehouseId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
});
return select.prop("outerHTML");
}
function buildLocationSelect(value, row, index) {
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newLocationId",
required: true
});
select.append($("<option>").val("").text("请选择新位置"));
$.each(locationList, function(i, location) {
if (String(location.warehouseId) === String(row.newWarehouseId)) {
var option = $("<option>").val(location.locationId)
.text(location.locationCode + " - " + location.locationName);
if (String(location.locationId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
}
});
return select.prop("outerHTML");
}
/* 在库资产无需设置部门,隐藏字段提交空值,后端强制清空以防伪造 */
function buildDeptSelect(value, row, index) {
if (row.assetStatus !== "IN_USE") {
return buildHidden("amsTransferOrderItemList[" + index + "].newDeptId", "") + "在库无需设置";
}
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newDeptId",
required: true,
onchange: "changeDept(" + index + ", this.value)"
});
select.append($("<option>").val("").text("请选择新部门"));
$.each(deptList, function(i, dept) {
var option = $("<option>").val(dept.deptId).text(dept.deptName);
if (String(dept.deptId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
});
return select.prop("outerHTML");
}
/* 在库资产无需设置使用人,同部门逻辑 */
function buildUserSelect(value, row, index) {
if (row.assetStatus !== "IN_USE") {
return buildHidden("amsTransferOrderItemList[" + index + "].newUserId", "") + "在库无需设置";
}
var select = $("<select>").addClass("form-control").attr({
name: "amsTransferOrderItemList[" + index + "].newUserId",
required: true
});
select.append($("<option>").val("").text("请选择新使用人"));
$.each(userList, function(i, user) {
if (String(user.deptId) === String(row.newDeptId)) {
var option = $("<option>").val(user.userId).text(user.userName + "" + user.loginName + "");
if (String(user.userId) === String(value)) {
option.attr("selected", "selected");
}
select.append(option);
}
});
return select.prop("outerHTML");
}
function buildHidden(name, value) {
return $("<input>").attr({ type: "hidden", name: name, value: value }).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>
</html>

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('选择可调拨资产')" />
</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="assetCode">
</li>
<li>
<label>资产名称:</label>
<input type="text" name="assetName">
</li>
<li>
<label>资产类别:</label>
<input type="text" name="categoryName">
</li>
<li>
<label>资产状态:</label>
<select name="assetStatus" th:with="type=${@dict.getType('ams_asset_status')}">
<option value="">所有</option>
<option th:each="dict : ${type}"
th:if="${dict.dictValue == 'IN_STOCK' || dict.dictValue == 'IN_USE'}"
th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()">
<i class="fa fa-search"></i>&nbsp;搜索
</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()">
<i class="fa fa-refresh"></i>&nbsp;重置
</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "asset/transfer";
var orderId = [[${orderId}]];
var assetStatusDatas = [[${@dict.getType('ams_asset_status')}]];
$(function() {
var url = prefix + "/availableAssetList";
if (orderId) {
url += "?orderId=" + orderId;
}
$.table.init({
url: url,
showSearch: false,
showRefresh: true,
showToggle: false,
showColumns: false,
modalName: "可调拨资产",
columns: [{
checkbox: true
},
{
field: "assetCode",
title: "资产编码"
},
{
field: "assetName",
title: "资产名称"
},
{
field: "categoryName",
title: "资产类别"
},
{
field: "assetStatus",
title: "资产状态",
formatter: function(value) {
return $.table.selectDictLabel(assetStatusDatas, value);
}
},
{
field: "warehouseName",
title: "当前仓库"
},
{
field: "locationName",
title: "当前位置"
},
{
field: "useDeptName",
title: "当前部门"
},
{
field: "useUserName",
title: "当前使用人"
}]
});
});
function getSelectedAssets() {
return $("#bootstrap-table").bootstrapTable("getSelections");
}
</script>
</body>
</html>

@ -1,10 +1,11 @@
<!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 :: header('资产调拨列表')" />
<th:block th:include="include :: datetimepicker-css" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
@ -15,35 +16,52 @@
<input type="text" name="transferNo"/>
</li>
<li>
<label>申请人ID</label>
<input type="text" name="applicantId"/>
</li>
<li>
<label>申请人名称快照:</label>
<label>申请人:</label>
<input type="text" name="applicantName"/>
</li>
<li>
<label>确认人ID</label>
<input type="text" name="confirmUserId"/>
</li>
<li>
<label>确认人名称快照:</label>
<input type="text" name="confirmUserName"/>
</li>
<li>
<li class="select-time">
<label>确认时间:</label>
<input type="text" class="time-input" placeholder="请选择确认时间" name="confirmTime"/>
<input type="text" class="time-input" id="startTime" placeholder="开始时间"
name="params[beginConfirmTime]"/>
<span>-</span>
<input type="text" class="time-input" id="endTime" placeholder="结束时间"
name="params[endConfirmTime]"/>
</li>
<li>
<label>单据状态:</label>
<select name="orderStatus" th:with="type=${@dict.getType('ams_transfer_status')}">
<option value="">所有</option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}"
th:value="${dict.dictValue}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
<label>资产编码:</label>
<input type="text" name="params[assetCode]"/>
</li>
<li>
<label>原仓库:</label>
<select name="params[oldWarehouseId]">
<option value="">所有</option>
<option th:each="warehouse : ${warehouseList}" th:value="${warehouse.warehouseId}"
th:text="${warehouse.warehouseCode + ' - ' + warehouse.warehouseName}"></option>
</select>
</li>
<li>
<label>新仓库:</label>
<select name="params[newWarehouseId]">
<option value="">所有</option>
<option th:each="warehouse : ${warehouseList}" th:value="${warehouse.warehouseId}"
th:text="${warehouse.warehouseCode + ' - ' + warehouse.warehouseName}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()">
<i class="fa fa-search"></i>&nbsp;搜索
</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()">
<i class="fa fa-refresh"></i>&nbsp;重置
</a>
</li>
</ul>
</div>
@ -54,12 +72,6 @@
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="asset:transfer:add">
<i class="fa fa-plus"></i> 添加
</a>
<a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="asset:transfer:edit">
<i class="fa fa-edit"></i> 修改
</a>
<a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="asset:transfer:remove">
<i class="fa fa-remove"></i> 删除
</a>
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="asset:transfer:export">
<i class="fa fa-download"></i> 导出
</a>
@ -70,25 +82,25 @@
</div>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: datetimepicker-js" />
<script th:inline="javascript">
var editFlag = [[${@permission.hasPermi('asset:transfer:edit')}]];
var removeFlag = [[${@permission.hasPermi('asset:transfer:remove')}]];
var submitFlag = [[${@permission.hasPermi('asset:transfer:submit')}]];
var confirmFlag = [[${@permission.hasPermi('asset:transfer:confirm')}]];
var orderStatusDatas = [[${@dict.getType('ams_transfer_status')}]];
var prefix = ctx + "asset/transfer";
$(function() {
var options = {
$.table.init({
url: prefix + "/list",
viewUrl: prefix + "/view/{id}",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
removeUrl: prefix + "/remove",
exportUrl: prefix + "/export",
modalName: "调拨管理",
modalName: "调拨",
columns: [{
checkbox: true
},
{
field: 'orderId',
title: '单据ID',
visible: false
@ -97,25 +109,17 @@
field: 'transferNo',
title: '调拨单号'
},
{
field: 'applicantId',
title: '申请人ID'
},
{
field: 'applicantName',
title: '申请人名称快照'
title: '申请人'
},
{
field: 'transferReason',
title: '调拨原因'
},
{
field: 'confirmUserId',
title: '确认人ID'
},
{
field: 'confirmUserName',
title: '确认人名称快照'
title: '确认人'
},
{
field: 'confirmTime',
@ -124,28 +128,51 @@
{
field: 'orderStatus',
title: '单据状态',
formatter: function(value, row, index) {
return $.table.selectDictLabel(orderStatusDatas, value);
formatter: function(value) {
return $.table.selectDictLabel(orderStatusDatas, value);
}
},
{
field: 'remark',
title: '备注'
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
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> ');
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-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.orderId + '\')"><i class="fa fa-remove"></i>删除</a>');
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 ' + submitFlag
+ '" href="javascript:void(0)" onclick="submitTransfer(\'' + row.orderId
+ '\')"><i class="fa fa-upload"></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>');
} else if (row.orderStatus === "PENDING_CONFIRM") {
actions.push('<a class="btn btn-primary btn-xs ' + confirmFlag
+ '" href="javascript:void(0)" onclick="confirmTransfer(\'' + row.orderId
+ '\')"><i class="fa fa-check"></i>确认调拨</a>');
}
return actions.join('');
}
}]
};
$.table.init(options);
});
});
/* 提交调拨单DRAFT → PENDING_CONFIRM提交后不可再修改或删除 */
function submitTransfer(orderId) {
$.modal.confirm("提交后调拨单不可再修改或删除,是否继续?", function() {
$.operate.post(prefix + "/submit/" + orderId, {});
});
}
/* 确认调拨PENDING_CONFIRM → TRANSFERRED会更新全部资产的当前归属并写入履历 */
function confirmTransfer(orderId) {
$.modal.confirm("确认后将更新全部资产的当前归属并写入履历,是否继续?", function() {
$.operate.post(prefix + "/confirm/" + orderId, {});
});
}
</script>
</body>
</html>
</html>

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('调拨管理详细')" />
<th:block th:include="include :: header('调拨单详情')" />
</head>
<body>
<div class="main-content">
@ -18,9 +18,9 @@
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人ID</label>
<label class="col-sm-4 control-label">申请人:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{applicantId}"></p>
<p class="form-control-plaintext" th:text="*{applicantName}"></p>
</div>
</div>
</div>
@ -28,9 +28,29 @@
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人名称快照</label>
<label class="col-sm-4 control-label">单据状态</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{applicantName}"></p>
<p class="form-control-plaintext"
th:text="*{@dict.getLabel('ams_transfer_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="*{confirmUserName}"></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="*{confirmTime == null ? '' : #dates.format(confirmTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
</div>
</div>
</div>
@ -44,51 +64,49 @@
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-4 control-label">确认人ID</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{confirmUserId}"></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="*{confirmUserName}"></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="*{#dates.format(confirmTime, '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="*{@dict.getLabel('ams_transfer_status', orderStatus)}"></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">
<label class="col-sm-2 control-label">备注:</label>
<div class="col-sm-10">
<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 : *{amsTransferOrderItemList}">
<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.oldWarehouseName ?: '-') + ' / ' + (item.oldLocationName ?: '-')}"></td>
<td th:text="${(item.newWarehouseName ?: '-') + ' / ' + (item.newLocationName ?: '-')}"></td>
<td th:text="${(item.oldDeptName ?: '-') + ' / ' + (item.oldUserName ?: '-')}"></td>
<td th:text="${(item.newDeptName ?: '-') + ' / ' + (item.newUserName ?: '-')}"></td>
<td th:text="${item.remark}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />

@ -0,0 +1,327 @@
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.assertNull;
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.TransferOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.domain.AmsTransferOrderItem;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsTransferOrderMapper;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.asset.service.IAssetStatusTransitionService;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.ISysCodeRuleService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
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 AmsTransferOrderServiceImplTest
{
@Mock
private AmsTransferOrderMapper amsTransferOrderMapper;
@Mock
private AmsAssetMapper amsAssetMapper;
@Mock
private ISysCodeRuleService sysCodeRuleService;
@Mock
private IAmsWarehouseService amsWarehouseService;
@Mock
private IAmsAssetLocationService amsAssetLocationService;
@Mock
private ISysDeptService sysDeptService;
@Mock
private ISysUserService sysUserService;
@Mock
private IAssetStatusTransitionService assetStatusTransitionService;
@InjectMocks
private AmsTransferOrderServiceImpl service;
/** 在库资产新增调拨草稿时,应生成单号、回填仓位快照并清空目标使用归属。 */
@Test
void insertStockTransferShouldGenerateCodeAndFillSnapshots()
{
AmsTransferOrder order = buildRequest(AssetStatus.IN_STOCK);
AmsTransferOrderItem item = order.getAmsTransferOrderItemList().get(0);
item.setNewDeptId(103L);
item.setNewUserId(1L);
when(sysCodeRuleService.nextCode("TRANSFER_ORDER")).thenReturn("DB202606120001");
stubStockAsset();
stubTargetWarehouseLocation();
doAnswer(invocation -> {
AmsTransferOrder inserted = invocation.getArgument(0);
inserted.setOrderId(100L);
return 1;
}).when(amsTransferOrderMapper).insertAmsTransferOrder(any(AmsTransferOrder.class));
when(amsTransferOrderMapper.batchAmsTransferOrderItem(anyList()))
.thenAnswer(invocation -> ((List<?>) invocation.getArgument(0)).size());
assertEquals(1, service.insertAmsTransferOrder(order, 1L, "管理员"));
assertEquals("DB202606120001", order.getTransferNo());
assertEquals(TransferOrderStatus.DRAFT, order.getOrderStatus());
assertEquals(1L, order.getApplicantId());
assertEquals("WH-001", item.getOldWarehouseCode());
assertEquals("WH-002", item.getNewWarehouseCode());
assertNull(item.getNewDeptId());
assertNull(item.getNewUserId());
assertEquals(100L, item.getOrderId());
assertNotNull(item.getCreateTime());
}
/** 在用资产调拨必须回填有效的新部门和新使用人快照。 */
@Test
void insertInUseTransferShouldFillNewUseOwnership()
{
AmsTransferOrder order = buildRequest(AssetStatus.IN_USE);
when(sysCodeRuleService.nextCode("TRANSFER_ORDER")).thenReturn("DB202606120001");
stubInUseAsset();
stubTargetWarehouseLocation();
stubTargetDeptUser();
doAnswer(invocation -> {
AmsTransferOrder inserted = invocation.getArgument(0);
inserted.setOrderId(100L);
return 1;
}).when(amsTransferOrderMapper).insertAmsTransferOrder(any(AmsTransferOrder.class));
when(amsTransferOrderMapper.batchAmsTransferOrderItem(anyList())).thenReturn(1);
service.insertAmsTransferOrder(order, 1L, "管理员");
AmsTransferOrderItem item = order.getAmsTransferOrderItemList().get(0);
assertEquals("研发部门", item.getNewDeptName());
assertEquals("管理员", item.getNewUserName());
}
/** 被其他草稿或待确认调拨单占用的资产不得再次加入调拨单。 */
@Test
void insertShouldRejectAssetOccupiedByOtherActiveTransfer()
{
AmsTransferOrder order = buildRequest(AssetStatus.IN_STOCK);
when(sysCodeRuleService.nextCode("TRANSFER_ORDER")).thenReturn("DB202606120001");
stubStockAsset();
when(amsTransferOrderMapper.countOtherActiveTransferOrderByAssetId(1L, null,
TransferOrderStatus.DRAFT, TransferOrderStatus.PENDING_CONFIRM)).thenReturn(1);
ServiceException exception = assertThrows(ServiceException.class,
() -> service.insertAmsTransferOrder(order, 1L, "管理员"));
assertTrue(exception.getMessage().contains("其他有效调拨单"));
verify(amsTransferOrderMapper, never()).insertAmsTransferOrder(any(AmsTransferOrder.class));
}
/** 草稿提交前校验当前归属未变化,成功后进入待确认状态。 */
@Test
void submitShouldValidateAndMoveToPendingConfirm()
{
AmsTransferOrder order = buildPersistedOrder(TransferOrderStatus.DRAFT, AssetStatus.IN_STOCK);
when(amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(100L)).thenReturn(order);
stubStockAsset();
stubTargetWarehouseLocation();
when(amsTransferOrderMapper.submitAmsTransferOrder(any(AmsTransferOrder.class))).thenReturn(1);
assertEquals(1, service.submitTransfer(100L, "admin"));
assertEquals(TransferOrderStatus.PENDING_CONFIRM, order.getOrderStatus());
verify(amsTransferOrderMapper).submitAmsTransferOrder(order);
}
/** 待确认调拨单确认时,应逐条调用公共流转服务并写入确认信息。 */
@Test
void confirmShouldDelegateTransitionAndCompleteOrder()
{
AmsTransferOrder order = buildPersistedOrder(TransferOrderStatus.PENDING_CONFIRM, AssetStatus.IN_STOCK);
when(amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(100L)).thenReturn(order);
stubStockAsset();
stubTargetWarehouseLocation();
when(amsTransferOrderMapper.confirmAmsTransferOrder(any(AmsTransferOrder.class))).thenReturn(1);
assertEquals(1, service.confirmTransfer(100L, 1L, "管理员", "admin"));
ArgumentCaptor<AssetTransitionContext> contextCaptor = ArgumentCaptor.forClass(AssetTransitionContext.class);
verify(assetStatusTransitionService).confirmTransfer(
org.mockito.ArgumentMatchers.eq(1L),
org.mockito.ArgumentMatchers.eq(2L),
org.mockito.ArgumentMatchers.eq(20L),
org.mockito.ArgumentMatchers.isNull(),
org.mockito.ArgumentMatchers.isNull(),
contextCaptor.capture());
assertEquals(100L, contextCaptor.getValue().getSourceOrderId());
assertEquals(101L, contextCaptor.getValue().getSourceItemId());
assertEquals(TransferOrderStatus.TRANSFERRED, order.getOrderStatus());
assertEquals("管理员", order.getConfirmUserName());
assertNotNull(order.getConfirmTime());
}
/** 调拨草稿保存后的原归属发生变化时,提交必须阻断,避免覆盖新归属。 */
@Test
void submitShouldRejectStaleOldOwnership()
{
AmsTransferOrder order = buildPersistedOrder(TransferOrderStatus.DRAFT, AssetStatus.IN_STOCK);
order.getAmsTransferOrderItemList().get(0).setOldLocationId(99L);
when(amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(100L)).thenReturn(order);
stubStockAsset();
ServiceException exception = assertThrows(ServiceException.class,
() -> service.submitTransfer(100L, "admin"));
assertTrue(exception.getMessage().contains("当前归属已变化"));
verify(amsTransferOrderMapper, never()).submitAmsTransferOrder(any(AmsTransferOrder.class));
}
/** 非草稿调拨单不得删除。 */
@Test
void deleteShouldRejectNonDraftOrder()
{
AmsTransferOrder order = new AmsTransferOrder();
order.setOrderId(100L);
order.setOrderStatus(TransferOrderStatus.PENDING_CONFIRM);
when(amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(100L)).thenReturn(order);
ServiceException exception = assertThrows(ServiceException.class,
() -> service.deleteAmsTransferOrderByOrderId(100L));
assertTrue(exception.getMessage().contains("仅草稿"));
verify(amsTransferOrderMapper, never()).deleteAmsTransferOrderByOrderId(100L);
}
private AmsTransferOrder buildRequest(String assetStatus)
{
AmsTransferOrder order = new AmsTransferOrder();
order.setCreateBy("admin");
order.setTransferReason("调整归属");
AmsTransferOrderItem item = new AmsTransferOrderItem();
item.setAssetId(1L);
item.setAssetStatus(assetStatus);
item.setNewWarehouseId(2L);
item.setNewLocationId(20L);
if (AssetStatus.IN_USE.equals(assetStatus))
{
item.setNewDeptId(103L);
item.setNewUserId(1L);
}
order.setAmsTransferOrderItemList(Collections.singletonList(item));
return order;
}
private AmsTransferOrder buildPersistedOrder(String orderStatus, String assetStatus)
{
AmsTransferOrder order = buildRequest(assetStatus);
order.setOrderId(100L);
order.setTransferNo("DB202606120001");
order.setOrderStatus(orderStatus);
AmsTransferOrderItem item = order.getAmsTransferOrderItemList().get(0);
item.setItemId(101L);
item.setOldWarehouseId(1L);
item.setOldLocationId(10L);
if (AssetStatus.IN_USE.equals(assetStatus))
{
item.setOldDeptId(105L);
item.setOldUserId(2L);
}
return order;
}
private void stubStockAsset()
{
AmsAsset asset = buildAsset(AssetStatus.IN_STOCK);
when(amsAssetMapper.selectAmsAssetByAssetIdForUpdate(1L)).thenReturn(asset);
}
private void stubInUseAsset()
{
AmsAsset asset = buildAsset(AssetStatus.IN_USE);
asset.setUseDeptId(105L);
asset.setUseDeptName("测试部门");
asset.setUseUserId(2L);
asset.setUseUserName("测试用户");
when(amsAssetMapper.selectAmsAssetByAssetIdForUpdate(1L)).thenReturn(asset);
}
private AmsAsset buildAsset(String status)
{
AmsAsset asset = new AmsAsset();
asset.setAssetId(1L);
asset.setAssetCode("ASSET-001");
asset.setAssetName("测试资产");
asset.setCategoryId(3L);
asset.setCategoryCode("CAT-003");
asset.setCategoryName("测试类别");
asset.setAssetStatus(status);
asset.setWarehouseId(1L);
asset.setWarehouseCode("WH-001");
asset.setWarehouseName("一号仓");
asset.setLocationId(10L);
asset.setLocationCode("LOC-010");
asset.setLocationName("一号仓A区");
return asset;
}
private void stubTargetWarehouseLocation()
{
AmsWarehouse warehouse = new AmsWarehouse();
warehouse.setWarehouseId(2L);
warehouse.setWarehouseCode("WH-002");
warehouse.setWarehouseName("二号仓");
warehouse.setEnabled("Y");
when(amsWarehouseService.selectAmsWarehouseByWarehouseId(2L)).thenReturn(warehouse);
AmsAssetLocation location = new AmsAssetLocation();
location.setLocationId(20L);
location.setLocationCode("LOC-020");
location.setLocationName("二号仓A区");
location.setWarehouseId(2L);
location.setEnabled("Y");
when(amsAssetLocationService.selectAmsAssetLocationByLocationId(20L)).thenReturn(location);
}
private void stubTargetDeptUser()
{
SysDept dept = new SysDept();
dept.setDeptId(103L);
dept.setDeptName("研发部门");
dept.setStatus("0");
dept.setDelFlag("0");
when(sysDeptService.selectDeptById(103L)).thenReturn(dept);
SysUser user = new SysUser();
user.setUserId(1L);
user.setUserName("管理员");
user.setDeptId(103L);
user.setStatus("0");
user.setDelFlag("0");
when(sysUserService.selectUserById(1L)).thenReturn(user);
}
}
Loading…
Cancel
Save