feat:出库单修改功能
This commit is contained in:
@@ -70,10 +70,10 @@ public class IostorInvController {
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新出入库单主表")
|
||||
@Operation(summary = "更新出库单主表及明细")
|
||||
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||
public CommonResult<Boolean> updateIostorInv(@Valid @RequestBody IostorInvSaveReqVO updateReqVO) {
|
||||
iostorInvService.updateIostorInv(updateReqVO);
|
||||
public CommonResult<Boolean> updateIostorInv(@Valid @RequestBody IostorInvUpdateReqVO reqVO) {
|
||||
iostorInvService.updateOutbound(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@@ -99,9 +99,20 @@ public class IostorInvController {
|
||||
@Operation(summary = "获得出入库单主表")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
||||
public CommonResult<IostorInvRespVO> getIostorInv(@RequestParam("id") String id) {
|
||||
public CommonResult<IostorInvRespVO> getIostorInv(@RequestParam("id") Long id) {
|
||||
IostorInvDO iostorInv = iostorInvService.getIostorInv(id);
|
||||
return success(BeanUtils.toBean(iostorInv, IostorInvRespVO.class));
|
||||
IostorInvRespVO respVO = BeanUtils.toBean(iostorInv, IostorInvRespVO.class);
|
||||
respVO.setDetails(iostorInvService.getIostorInvDetails(id));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/details")
|
||||
@Operation(summary = "获得出入库单全部明细")
|
||||
@Parameter(name = "iostorinvId", description = "出入库单主表标识", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
||||
public CommonResult<List<IostorInvDetailRespVO>> getIostorInvDetails(
|
||||
@RequestParam("iostorinvId") Long iostorinvId) {
|
||||
return success(iostorInvService.getIostorInvDetails(iostorinvId));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
|
||||
@@ -19,6 +19,10 @@ public class IostorInvCreateReqVO {
|
||||
@NotEmpty(message = "单据类型不能为空")
|
||||
private String billType;
|
||||
|
||||
@Schema(description = "单据状态", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "单据状态不能为空")
|
||||
private String billStatus;
|
||||
|
||||
@Schema(description = "仓库标识", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "仓库标识不能为空")
|
||||
private String storId;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.code.nl.module.wms.controller.admin.iostorinv.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 出入库单编辑明细 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class IostorInvDetailRespVO extends IostorInvCreateReqVO.Detail {
|
||||
|
||||
@Schema(description = "物料名称")
|
||||
private String materialName;
|
||||
}
|
||||
@@ -130,4 +130,7 @@ public class IostorInvRespVO implements VO {
|
||||
@ExcelProperty("回传时间")
|
||||
private String uploadTime;
|
||||
|
||||
@Schema(description = "出入库单明细")
|
||||
private List<IostorInvDetailRespVO> details;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.code.nl.module.wms.controller.admin.iostorinv.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 出库单修改 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class IostorInvUpdateReqVO extends IostorInvCreateReqVO {
|
||||
|
||||
@Schema(description = "出入库单主表标识", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "出入库单主表标识不能为空")
|
||||
private Long iostorinvId;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryPageReqVO;
|
||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryRespVO;
|
||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvDetailRespVO;
|
||||
import cn.code.nl.module.wms.controller.admin.iostorinvdtl.vo.*;
|
||||
|
||||
/**
|
||||
@@ -47,6 +48,9 @@ public interface IostorinvDtlMapper extends BaseMapperX<IostorinvDtlDO> {
|
||||
List<AvailableInventoryRespVO> selectAvailableInventoryByVehicleCodesForUpdate(
|
||||
@Param("storId") String storId, @Param("vehicleCodes") Collection<String> vehicleCodes);
|
||||
|
||||
/** 查询指定出入库单的编辑明细,并带出物料名称。 */
|
||||
List<IostorInvDetailRespVO> selectEditDetails(@Param("iostorinvId") Long iostorinvId);
|
||||
|
||||
default PageResult<IostorinvDtlDO> selectPage(IostorinvDtlPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||
.eqIfPresent(IostorinvDtlDO::getIostorinvId, reqVO.getIostorinvId())
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.*;
|
||||
import jakarta.validation.*;
|
||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.*;
|
||||
import cn.code.nl.module.wms.dal.dataobject.iostorinv.IostorInvDO;
|
||||
import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO;
|
||||
import cn.code.nl.framework.common.pojo.PageResult;
|
||||
import cn.code.nl.framework.common.pojo.PageParam;
|
||||
|
||||
@@ -53,6 +54,11 @@ public interface IostorInvService {
|
||||
*/
|
||||
void updateIostorInv(@Valid IostorInvSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新出库单主表并替换全部明细。
|
||||
*/
|
||||
void updateOutbound(@Valid IostorInvUpdateReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 删除出入库单主表
|
||||
*
|
||||
@@ -73,7 +79,12 @@ public interface IostorInvService {
|
||||
* @param id 编号
|
||||
* @return 出入库单主表
|
||||
*/
|
||||
IostorInvDO getIostorInv(String id);
|
||||
IostorInvDO getIostorInv(Long id);
|
||||
|
||||
/**
|
||||
* 获得出入库单的全部明细。
|
||||
*/
|
||||
List<IostorInvDetailRespVO> getIostorInvDetails(Long id);
|
||||
|
||||
/**
|
||||
* 获得出入库单主表分页
|
||||
|
||||
@@ -90,16 +90,24 @@ public class IostorInvServiceImpl implements IostorInvService {
|
||||
iostorInv.setBillType(reqVO.getBillType());
|
||||
iostorInv.setBizDate(reqVO.getBizDate());
|
||||
iostorInv.setStorId(reqVO.getStorId());
|
||||
iostorInv.setBillStatus("10");
|
||||
iostorInv.setBillStatus(reqVO.getBillStatus());
|
||||
iostorInv.setRemark(reqVO.getRemark());
|
||||
iostorInv.setDetailCount(reqVO.getDetails().size());
|
||||
iostorInv.setTotalWeight(totalWeight);
|
||||
iostorInvMapper.insert(iostorInv);
|
||||
|
||||
for (int index = 0; index < reqVO.getDetails().size(); index++) {
|
||||
IostorInvCreateReqVO.Detail detail = reqVO.getDetails().get(index);
|
||||
insertOutboundDetails(iostorInv.getIostorinvId(), reqVO.getBillStatus(),
|
||||
reqVO.getDetails(), inventorySnapshot);
|
||||
return iostorInv.getIostorinvId();
|
||||
}
|
||||
|
||||
private void insertOutboundDetails(Long iostorinvId, String billStatus,
|
||||
List<IostorInvCreateReqVO.Detail> details,
|
||||
Map<Long, AvailableInventoryRespVO> inventorySnapshot) {
|
||||
for (int index = 0; index < details.size(); index++) {
|
||||
IostorInvCreateReqVO.Detail detail = details.get(index);
|
||||
IostorinvDtlDO detailDO = new IostorinvDtlDO();
|
||||
detailDO.setIostorinvId(iostorInv.getIostorinvId());
|
||||
detailDO.setIostorinvId(iostorinvId);
|
||||
detailDO.setSeqNo(index + 1);
|
||||
AvailableInventoryRespVO inventory = inventorySnapshot.get(detail.getGroupId());
|
||||
detailDO.setMaterialCode(inventory == null ? detail.getMaterialCode() : inventory.getMaterialCode());
|
||||
@@ -108,7 +116,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
||||
detailDO.setPlanQty(detail.getPlanQty());
|
||||
detailDO.setUnassignQty(detail.getPlanQty());
|
||||
detailDO.setAssignQty(BigDecimal.ZERO);
|
||||
detailDO.setBillStatus("10");
|
||||
detailDO.setBillStatus(billStatus);
|
||||
detailDO.setQtyUnitId(inventory == null ? detail.getQtyUnitId() : inventory.getQtyUnitId());
|
||||
detailDO.setSourceBillCode(inventory == null ? detail.getSourceBillCode() : inventory.getExtCode());
|
||||
detailDO.setSourceBillType(inventory == null ? detail.getSourceBillType() : inventory.getExtType());
|
||||
@@ -116,7 +124,6 @@ public class IostorInvServiceImpl implements IostorInvService {
|
||||
detailDO.setRemark(detail.getRemark());
|
||||
iostorinvDtlMapper.insert(detailDO);
|
||||
}
|
||||
return iostorInv.getIostorinvId();
|
||||
}
|
||||
|
||||
private Map<Long, AvailableInventoryRespVO> validateAndLockInventory(IostorInvCreateReqVO reqVO) {
|
||||
@@ -199,6 +206,32 @@ public class IostorInvServiceImpl implements IostorInvService {
|
||||
iostorInvMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOutbound(IostorInvUpdateReqVO reqVO) {
|
||||
validateIostorInvExists(String.valueOf(reqVO.getIostorinvId()));
|
||||
Map<Long, AvailableInventoryRespVO> inventorySnapshot = validateAndLockInventory(reqVO);
|
||||
BigDecimal totalWeight = reqVO.getDetails().stream()
|
||||
.map(IostorInvCreateReqVO.Detail::getPlanQty)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
IostorInvDO updateObj = new IostorInvDO();
|
||||
updateObj.setIostorinvId(reqVO.getIostorinvId());
|
||||
updateObj.setBillType(reqVO.getBillType());
|
||||
updateObj.setBillStatus(reqVO.getBillStatus());
|
||||
updateObj.setBizDate(reqVO.getBizDate());
|
||||
updateObj.setStorId(reqVO.getStorId());
|
||||
updateObj.setRemark(reqVO.getRemark());
|
||||
updateObj.setDetailCount(reqVO.getDetails().size());
|
||||
updateObj.setTotalWeight(totalWeight);
|
||||
iostorInvMapper.updateById(updateObj);
|
||||
|
||||
iostorinvDtlMapper.delete(new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||
.eq(IostorinvDtlDO::getIostorinvId, reqVO.getIostorinvId()));
|
||||
insertOutboundDetails(reqVO.getIostorinvId(), reqVO.getBillStatus(),
|
||||
reqVO.getDetails(), inventorySnapshot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteIostorInv(String id) {
|
||||
// 校验存在
|
||||
@@ -221,10 +254,15 @@ public class IostorInvServiceImpl implements IostorInvService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IostorInvDO getIostorInv(String id) {
|
||||
public IostorInvDO getIostorInv(Long id) {
|
||||
return iostorInvMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IostorInvDetailRespVO> getIostorInvDetails(Long id) {
|
||||
return iostorinvDtlMapper.selectEditDetails(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IostorInvDO> getIostorInvPage(IostorInvPageReqVO pageReqVO) {
|
||||
PageResult<IostorInvDO> pageResult = iostorInvMapper.selectPage(pageReqVO);
|
||||
|
||||
@@ -92,4 +92,25 @@
|
||||
FOR UPDATE
|
||||
</select>
|
||||
|
||||
<select id="selectEditDetails"
|
||||
resultType="cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvDetailRespVO">
|
||||
SELECT d.material_code,
|
||||
d.material_id,
|
||||
mb.material_name,
|
||||
d.pcsn,
|
||||
d.plan_qty,
|
||||
d.qty_unit_id,
|
||||
d.source_bill_code,
|
||||
d.source_bill_type,
|
||||
d.source_billdtl_id,
|
||||
d.remark
|
||||
FROM wms_iostorinvdtl d
|
||||
LEFT JOIN base_materialbase mb
|
||||
ON mb.material_id = d.material_id
|
||||
AND mb.deleted = 0
|
||||
WHERE d.iostorinv_id = #{iostorinvId}
|
||||
AND d.deleted = 0
|
||||
ORDER BY d.seq_no, d.iostorinvdtl_id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -64,10 +64,9 @@ class IostorInvServiceLocalSpringTest {
|
||||
|
||||
@Test
|
||||
void shouldCreateOutboundUsingLockedInventoryIdentityInsteadOfClientUnitAndSource() {
|
||||
String id = iostorInvService.createOutbound(buildRequest());
|
||||
Long id = iostorInvService.createOutbound(buildRequest());
|
||||
|
||||
assertNotNull(id);
|
||||
assertFalse(id.isBlank());
|
||||
jdbcTemplate.queryForObject("SELECT * FROM wms_iostorinv WHERE iostorinv_id = ?", (rs, rowNum) -> {
|
||||
assertEquals("OUT-TEST-0001", rs.getString("bill_code"));
|
||||
assertEquals("OUT", rs.getString("io_type"));
|
||||
@@ -97,12 +96,12 @@ class IostorInvServiceLocalSpringTest {
|
||||
assertFalse(details.get(0).detailId().isBlank());
|
||||
assertFalse(details.get(1).detailId().isBlank());
|
||||
assertNotEquals(details.get(0).detailId(), details.get(1).detailId());
|
||||
assertEquals(id, details.get(0).headerId());
|
||||
assertEquals(id, details.get(1).headerId());
|
||||
assertEquals(new DetailRow(details.get(0).detailId(), id, 1, "MAT-01", "MID-01", "PCSN-01",
|
||||
assertEquals(id.toString(), details.get(0).headerId());
|
||||
assertEquals(id.toString(), details.get(1).headerId());
|
||||
assertEquals(new DetailRow(details.get(0).detailId(), id.toString(), 1, "MAT-01", "MID-01", "PCSN-01",
|
||||
new BigDecimal("12.500"), BigDecimal.ZERO.setScale(3), new BigDecimal("12.500"),
|
||||
"DB-KG", "数据库千克", "DB-SRC-01", "DB-TYPE", "DB-DTL-01", "明细一"), details.get(0));
|
||||
assertEquals(new DetailRow(details.get(1).detailId(), id, 2, "MAT-02", "MID-02", "PCSN-02",
|
||||
assertEquals(new DetailRow(details.get(1).detailId(), id.toString(), 2, "MAT-02", "MID-02", "PCSN-02",
|
||||
new BigDecimal("7.250"), BigDecimal.ZERO.setScale(3), new BigDecimal("7.250"),
|
||||
"DB-KG", "数据库千克", "DB-SRC-02", "DB-TYPE", "DB-DTL-02", "明细二"), details.get(1));
|
||||
}
|
||||
@@ -218,6 +217,7 @@ class IostorInvServiceLocalSpringTest {
|
||||
private IostorInvCreateReqVO buildRequest() {
|
||||
IostorInvCreateReqVO request = new IostorInvCreateReqVO();
|
||||
request.setBillType("销售出库");
|
||||
request.setBillStatus("生成");
|
||||
request.setStorId("STOR-01");
|
||||
request.setBizDate(LocalDateTime.of(2026, 7, 22, 0, 0));
|
||||
request.setRemark("主表备注");
|
||||
|
||||
@@ -27,6 +27,7 @@ export namespace WmsIostorInvApi {
|
||||
creator?: string; // 创建者标识
|
||||
creatorName?: string; // 创建者名称
|
||||
createTime?: Dayjs | string; // 创建时间
|
||||
details?: OutboundDetail[]; // 出入库单明细
|
||||
disOptid: string; // 分配人
|
||||
disTime?: Dayjs | string; // 分配时间
|
||||
confirmOptid: string; // 确认人
|
||||
@@ -64,12 +65,18 @@ export namespace WmsIostorInvApi {
|
||||
/** 出库单新增请求 */
|
||||
export interface OutboundCreateReq {
|
||||
billType: string; // 单据类型
|
||||
billStatus: string; // 单据状态
|
||||
storId: string; // 仓库标识
|
||||
bizDate: number; // 业务日期(毫秒时间戳)
|
||||
remark?: string; // 备注
|
||||
details: OutboundDetail[]; // 出库单明细
|
||||
}
|
||||
|
||||
/** 出库单修改请求 */
|
||||
export interface OutboundUpdateReq extends OutboundCreateReq {
|
||||
iostorinvId: string;
|
||||
}
|
||||
|
||||
/** 可用库存 */
|
||||
export interface AvailableInventory {
|
||||
groupId: number | string; // 组盘记录标识
|
||||
@@ -119,6 +126,13 @@ export function getIostorInv(id: string) {
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询出入库单全部明细 */
|
||||
export function getIostorInvDetails(iostorinvId: string) {
|
||||
return requestClient.get<WmsIostorInvApi.OutboundDisplayDetail[]>(
|
||||
`/wms/iostor-inv/details?iostorinvId=${encodeURIComponent(iostorinvId)}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增出入库单主表 */
|
||||
export function createIostorInv(data: WmsIostorInvApi.IostorInv) {
|
||||
return requestClient.post('/wms/iostor-inv/create', data);
|
||||
@@ -129,6 +143,11 @@ export function updateIostorInv(data: WmsIostorInvApi.IostorInv) {
|
||||
return requestClient.put('/wms/iostor-inv/update', data);
|
||||
}
|
||||
|
||||
/** 修改出库单主表及明细 */
|
||||
export function updateOutbound(data: WmsIostorInvApi.OutboundUpdateReq) {
|
||||
return requestClient.put('/wms/iostor-inv/update', data);
|
||||
}
|
||||
|
||||
/** 删除出入库单主表 */
|
||||
export function deleteIostorInv(id: number) {
|
||||
return requestClient.delete(`/wms/iostor-inv/delete?id=${id}`);
|
||||
|
||||
@@ -9,37 +9,37 @@ import { Button, Input, InputNumber, message, Space, Table } from 'antdv-next';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getSimpleDictDataList } from '#/api/system/dict/data';
|
||||
import { getBsrealStorAttrSimpleList } from '#/api/wms/bsrealstorattr';
|
||||
import {
|
||||
createOutbound,
|
||||
getIostorInv,
|
||||
updateIostorInv,
|
||||
getIostorInvDetails,
|
||||
updateOutbound,
|
||||
} from '#/api/wms/iostorinv';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import { summarizeDetails } from './detail-summary';
|
||||
import InventorySelectComponent from './inventory-select.vue';
|
||||
import ManualDetailComponent from './manual-detail.vue';
|
||||
import {
|
||||
buildOutboundPayload,
|
||||
filterOutboundBillTypeOptions,
|
||||
mergeOutboundInventory,
|
||||
type OutboundFormDetail,
|
||||
shouldApplyEditResult,
|
||||
shouldClearDetailsForWarehouseChange,
|
||||
} from './outbound-form';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<WmsIostorInvApi.IostorInv>();
|
||||
const details = ref<OutboundFormDetail[]>([]);
|
||||
const currentStorId = ref<string>();
|
||||
let manualRowSequence = 0;
|
||||
let billTypeRequest: null | ReturnType<typeof getSimpleDictDataList> = null;
|
||||
let mainModalOpen = false;
|
||||
let mainOpenToken = 0;
|
||||
let activeEditId: string | undefined;
|
||||
let suppressWarehouseChange = false;
|
||||
|
||||
const isEdit = computed(() => Boolean(formData.value?.iostorinvId));
|
||||
const getTitle = computed(() =>
|
||||
@@ -48,17 +48,6 @@ const getTitle = computed(() =>
|
||||
: '出库单新增',
|
||||
);
|
||||
|
||||
const [LegacyForm, legacyFormApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: { class: 'w-full' },
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
async function handleWarehouseChange(nextStorId?: string) {
|
||||
const shouldClear = shouldClearDetailsForWarehouseChange(
|
||||
currentStorId.value,
|
||||
@@ -78,7 +67,7 @@ const [OutboundForm, outboundFormApi] = useVbenForm({
|
||||
labelWidth: 88,
|
||||
},
|
||||
handleValuesChange: (values) => {
|
||||
if (Object.hasOwn(values, 'storId')) {
|
||||
if (!suppressWarehouseChange && Object.hasOwn(values, 'storId')) {
|
||||
void handleWarehouseChange(values.storId);
|
||||
}
|
||||
},
|
||||
@@ -108,7 +97,7 @@ const [OutboundForm, outboundFormApi] = useVbenForm({
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: [],
|
||||
options: getDictOptions(DICT_TYPE.WMS_OUT_BILL_TYPE),
|
||||
placeholder: '请选择业务类型',
|
||||
},
|
||||
fieldName: 'billType',
|
||||
@@ -116,11 +105,15 @@ const [OutboundForm, outboundFormApi] = useVbenForm({
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
defaultValue: '生成',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.WMS_IO_BILL_STATUS),
|
||||
placeholder: '请选择单据状态',
|
||||
},
|
||||
fieldName: 'billStatus',
|
||||
label: '单据状态',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
@@ -164,28 +157,6 @@ const [ManualDetail, manualDetailApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
async function loadBillTypeOptions() {
|
||||
billTypeRequest ??= getSimpleDictDataList();
|
||||
const request = billTypeRequest;
|
||||
try {
|
||||
const dictItems = await request;
|
||||
outboundFormApi.updateSchema([
|
||||
{
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: filterOutboundBillTypeOptions(dictItems),
|
||||
placeholder: '请选择业务类型',
|
||||
},
|
||||
fieldName: 'billType',
|
||||
},
|
||||
]);
|
||||
} catch {
|
||||
message.error('出库业务类型加载失败,请稍后重试');
|
||||
} finally {
|
||||
if (billTypeRequest === request) billTypeRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ key: 'index', title: '序号', width: 60 },
|
||||
{ dataIndex: 'materialCode', key: 'materialCode', title: '物料编码', width: 130 },
|
||||
@@ -265,34 +236,23 @@ function validateDetails() {
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
if (isEdit.value) {
|
||||
const { valid } = await legacyFormApi.validate();
|
||||
if (!valid) return;
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateIostorInv(
|
||||
(await legacyFormApi.getValues()) as WmsIostorInvApi.IostorInv,
|
||||
);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { valid } = await outboundFormApi.validate();
|
||||
if (!valid || !validateDetails()) return;
|
||||
const values = await outboundFormApi.getValues();
|
||||
modalApi.lock();
|
||||
try {
|
||||
await createOutbound(
|
||||
buildOutboundPayload(
|
||||
const payload = buildOutboundPayload(
|
||||
{ ...values, bizDate: dayjs(values.bizDate).valueOf() },
|
||||
details.value,
|
||||
),
|
||||
);
|
||||
if (isEdit.value) {
|
||||
await updateOutbound({
|
||||
...payload,
|
||||
iostorinvId: formData.value!.iostorinvId,
|
||||
});
|
||||
} else {
|
||||
await createOutbound(payload);
|
||||
}
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
@@ -318,17 +278,32 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = undefined;
|
||||
details.value = [];
|
||||
currentStorId.value = undefined;
|
||||
suppressWarehouseChange = true;
|
||||
try {
|
||||
await outboundFormApi.resetForm();
|
||||
await outboundFormApi.updateSchema([
|
||||
{
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
disabled: false,
|
||||
options: getDictOptions(DICT_TYPE.WMS_IO_BILL_STATUS),
|
||||
placeholder: '请选择单据状态',
|
||||
},
|
||||
fieldName: 'billStatus',
|
||||
},
|
||||
]);
|
||||
if (!mainModalOpen || mainOpenToken !== token || activeEditId) return;
|
||||
await outboundFormApi.setValues({
|
||||
billCode: '保存时自动生成',
|
||||
billStatus: '生成',
|
||||
billStatus: '10',
|
||||
bizDate: dayjs(),
|
||||
detailCount: 0,
|
||||
totalWeight: '0.000',
|
||||
});
|
||||
} finally {
|
||||
suppressWarehouseChange = false;
|
||||
}
|
||||
if (!mainModalOpen || mainOpenToken !== token || activeEditId) return;
|
||||
await loadBillTypeOptions();
|
||||
return;
|
||||
}
|
||||
const editId = data.iostorinvId;
|
||||
@@ -336,7 +311,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = data;
|
||||
modalApi.lock();
|
||||
try {
|
||||
const editResult = await getIostorInv(editId);
|
||||
const [editResult, editDetails] = await Promise.all([
|
||||
getIostorInv(editId),
|
||||
getIostorInvDetails(editId),
|
||||
]);
|
||||
if (
|
||||
!shouldApplyEditResult(
|
||||
mainModalOpen,
|
||||
@@ -349,7 +327,38 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
formData.value = editResult;
|
||||
await legacyFormApi.setValues(editResult);
|
||||
suppressWarehouseChange = true;
|
||||
try {
|
||||
await outboundFormApi.resetForm();
|
||||
await outboundFormApi.updateSchema([
|
||||
{
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.WMS_IO_BILL_STATUS),
|
||||
placeholder: '请选择单据状态',
|
||||
},
|
||||
fieldName: 'billStatus',
|
||||
},
|
||||
]);
|
||||
details.value = editDetails.map((detail, index) => ({
|
||||
...detail,
|
||||
rowKey: `edit-${editId}-${index}`,
|
||||
}));
|
||||
await outboundFormApi.setValues({
|
||||
billCode: editResult.billCode,
|
||||
billStatus: editResult.billStatus,
|
||||
billType: editResult.billType,
|
||||
bizDate: editResult.bizDate ? dayjs(editResult.bizDate) : undefined,
|
||||
detailCount: details.value.length,
|
||||
remark: editResult.remark,
|
||||
storId: editResult.storId,
|
||||
totalWeight: Number(editResult.totalWeight || 0).toFixed(3),
|
||||
});
|
||||
currentStorId.value = editResult.storId;
|
||||
} finally {
|
||||
suppressWarehouseChange = false;
|
||||
}
|
||||
} finally {
|
||||
if (
|
||||
shouldApplyEditResult(
|
||||
@@ -369,8 +378,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal class="w-[1400px] max-w-[96vw]" :title="getTitle">
|
||||
<LegacyForm v-if="isEdit" class="mx-4" />
|
||||
<div v-else class="px-4">
|
||||
<div class="px-4">
|
||||
<OutboundForm />
|
||||
<div class="mb-3 mt-1 flex items-center justify-between">
|
||||
<span class="text-base font-medium">出库明细</span>
|
||||
|
||||
@@ -34,7 +34,13 @@ describe('buildOutboundPayload', () => {
|
||||
it('将业务日期转换成毫秒,并剥离所有仅展示字段和汇总字段', () => {
|
||||
const bizDate = new Date('2026-07-22T08:30:00+08:00');
|
||||
const payload = buildOutboundPayload(
|
||||
{ billType: 'SALE', bizDate, remark: '主备注', storId: 'S-1' },
|
||||
{
|
||||
billStatus: '10',
|
||||
billType: 'SALE',
|
||||
bizDate,
|
||||
remark: '主备注',
|
||||
storId: 'S-1',
|
||||
},
|
||||
[
|
||||
{
|
||||
groupId: 11,
|
||||
@@ -79,7 +85,7 @@ describe('buildOutboundPayload', () => {
|
||||
|
||||
it('手工汇总行不附带库存引用字段', () => {
|
||||
const payload = buildOutboundPayload(
|
||||
{ billType: 'SALE', bizDate: 1, storId: 'S-1' },
|
||||
{ billStatus: '10', billType: 'SALE', bizDate: 1, storId: 'S-1' },
|
||||
[{ materialCode: 'M-1', planQty: 2 }],
|
||||
);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export function shouldApplyEditResult(
|
||||
|
||||
export interface OutboundFormValues {
|
||||
billType?: string;
|
||||
billStatus?: string;
|
||||
bizDate?: number | string | { valueOf: () => number };
|
||||
remark?: string;
|
||||
storId?: string;
|
||||
@@ -48,6 +49,7 @@ export function buildOutboundPayload(
|
||||
): WmsIostorInvApi.OutboundCreateReq {
|
||||
return {
|
||||
billType: values.billType!,
|
||||
billStatus: values.billStatus!,
|
||||
bizDate: Number(values.bizDate?.valueOf()),
|
||||
details: details.map((detail) => ({
|
||||
groupId: detail.groupId,
|
||||
|
||||
Reference in New Issue
Block a user