From 04ffd865a368c35640848ef3892f9437e1f847a3 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Tue, 28 Jul 2026 16:21:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=87=BA=E5=BA=93=E5=8D=95=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD=E5=8F=8A=E7=A7=BB?= =?UTF-8?q?=E5=BA=93=E5=8D=95CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .image/出库新增.png | Bin 0 -> 194525 bytes .../plans/2026-07-27-wms-move-inventory.md | 86 ++++ .../2026-07-27-wms-move-inventory-design.md | 164 ++++++++ .../src/main/resources/application.yaml | 10 + .../nl/module/task/api/TransportTaskApi.java | 5 + .../task/dto/TransportTaskCreateReqDTO.java | 6 + .../module/task/api/TransportTaskApiImpl.java | 5 + .../transporttask/TransportTaskMapper.java | 8 + .../rpc/config/RpcConfiguration.java | 7 +- .../transporttask/TransportTaskService.java | 2 + .../TransportTaskServiceImpl.java | 5 + .../src/main/resources/application-dev.yaml | 16 +- .../src/main/resources/application.yaml | 2 +- .../nl/module/wms/api/moveinv/MoveInvApi.java | 19 + .../moveinv/dto/MoveInvAutoCreateReqDTO.java | 16 + .../moveinv/dto/MoveInvAutoCreateRespDTO.java | 17 + .../nl/module/wms/enums/ApiConstants.java | 15 + .../module/wms/enums/ErrorCodeConstants.java | 12 + .../module/wms/enums/OutboundPointEnum.java | 21 + nl-module-wms/nl-module-wms-server/pom.xml | 6 + .../wms/api/moveinv/MoveInvApiImpl.java | 30 ++ .../admin/iostorinv/IostorInvController.java | 8 + .../vo/OutboundOneClickSetReqVO.java | 16 + .../admin/moveinv/MoveInvController.java | 98 +++++ .../moveinv/vo/MoveInvDetailSaveReqVO.java | 29 ++ .../moveinv/vo/MoveInvInventoryRespVO.java | 28 ++ .../admin/moveinv/vo/MoveInvPageReqVO.java | 16 + .../admin/moveinv/vo/MoveInvRespVO.java | 37 ++ .../admin/moveinv/vo/MoveInvSaveReqVO.java | 27 ++ .../wms/dal/dataobject/moveinv/MoveInvDO.java | 33 ++ .../dataobject/moveinvdtl/MoveInvDtlDO.java | 35 ++ .../wms/dal/mysql/moveinv/MoveInvMapper.java | 24 ++ .../mysql/moveinvdtl/MoveInvDtlMapper.java | 19 + .../dal/mysql/structAttr/StrucAttrMapper.java | 34 ++ .../rpc/config/RpcConfiguration.java | 4 +- .../service/iostorinv/IostorInvService.java | 2 + .../iostorinv/IostorInvServiceImpl.java | 202 ++++++++++ .../iostorinv/OutboundPlacementPlanner.java | 64 +++ .../wms/service/moveinv/MoveInvService.java | 34 ++ .../service/moveinv/MoveInvServiceImpl.java | 370 ++++++++++++++++++ .../moveinv/MoveTargetStructSelector.java | 40 ++ .../mapper/moveinvdtl/MoveInvDtlMapper.xml | 17 + .../mapper/structAttr/StrucAttrMapper.xml | 111 +++++- .../OutboundPlacementPlannerTest.java | 47 +++ .../moveinv/MoveTargetStructSelectorTest.java | 34 ++ .../src/api/wms/iostorinv/index.ts | 7 + .../src/api/wms/moveinv/index.ts | 103 +++++ .../wms/iostorinv/modules/allocation.vue | 23 +- .../src/views/wms/moveinv/data.ts | 65 +++ .../src/views/wms/moveinv/index.vue | 127 ++++++ .../src/views/wms/moveinv/modules/detail.vue | 57 +++ .../src/views/wms/moveinv/modules/form.vue | 296 ++++++++++++++ .../src/views/wms/order/movement/index.vue | 7 + sql/mysql/wms_moveinv_lock_type.sql | 20 + sql/mysql/wms_moveinv_menu.sql | 85 ++++ sql/mysql/wms_one_click_set_test_data.sql | 90 +++++ 56 files changed, 2638 insertions(+), 23 deletions(-) create mode 100644 .image/出库新增.png create mode 100644 docs/superpowers/plans/2026-07-27-wms-move-inventory.md create mode 100644 docs/superpowers/specs/2026-07-27-wms-move-inventory-design.md create mode 100644 nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApi.java create mode 100644 nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateReqDTO.java create mode 100644 nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateRespDTO.java create mode 100644 nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ApiConstants.java create mode 100644 nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/OutboundPointEnum.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApiImpl.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/OutboundOneClickSetReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/MoveInvController.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvDetailSaveReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvInventoryRespVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvPageReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvRespVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvSaveReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinv/MoveInvDO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinvdtl/MoveInvDtlDO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinv/MoveInvMapper.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinvdtl/MoveInvDtlMapper.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlanner.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvService.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvServiceImpl.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelector.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinvdtl/MoveInvDtlMapper.xml create mode 100644 nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlannerTest.java create mode 100644 nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelectorTest.java create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/moveinv/index.ts create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/data.ts create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/index.vue create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/detail.vue create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/form.vue create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/order/movement/index.vue create mode 100644 sql/mysql/wms_moveinv_lock_type.sql create mode 100644 sql/mysql/wms_moveinv_menu.sql create mode 100644 sql/mysql/wms_one_click_set_test_data.sql diff --git a/.image/出库新增.png b/.image/出库新增.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9da66d10d6e67a57437832da5bdb4db174dc87 GIT binary patch literal 194525 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%0DP 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 +
+ 分配明细 + +
+import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { WmsMoveInvApi } from '#/api/wms/moveinv'; + +import { Page, useVbenModal } from '@vben/common-ui'; + +import { Button, message } from 'antdv-next'; + +import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; +import { deleteMoveInv, getMoveInvPage } from '#/api/wms/moveinv'; + +import { useGridColumns, useGridFormSchema } from './data'; +import Detail from './modules/detail.vue'; +import Form from './modules/form.vue'; + +const [FormModal, formModalApi] = useVbenModal({ + connectedComponent: Form, + destroyOnClose: true, +}); +const [DetailModal, detailModalApi] = useVbenModal({ + connectedComponent: Detail, + destroyOnClose: true, +}); + +function refresh() { + gridApi.query(); +} + +function create() { + formModalApi.setData(null).open(); +} + +function edit(row: WmsMoveInvApi.MoveInv) { + if (row.billStatus !== '10') { + message.warning('只有生成状态的移库单允许修改'); + return; + } + formModalApi.setData(row).open(); +} + +function view(row: WmsMoveInvApi.MoveInv) { + detailModalApi.setData(row).open(); +} + +async function remove(row: WmsMoveInvApi.MoveInv) { + if (row.billStatus !== '10') { + message.warning('只有生成状态的移库单允许删除'); + return; + } + await deleteMoveInv(row.moveinvId!); + message.success('删除成功'); + refresh(); +} + +const [Grid, gridApi] = useVbenVxeGrid({ + formOptions: { schema: useGridFormSchema() }, + gridOptions: { + columns: useGridColumns(), + height: 'auto', + proxyConfig: { + ajax: { + query: async ({ page }, values) => + await getMoveInvPage({ + ...values, + pageNo: page.currentPage, + pageSize: page.pageSize, + }), + }, + }, + rowConfig: { isHover: true, keyField: 'moveinvId' }, + toolbarConfig: { refresh: true, search: true }, + } as VxeTableGridOptions, +}); + + + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/detail.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/detail.vue new file mode 100644 index 00000000..10abf3df --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/detail.vue @@ -0,0 +1,57 @@ + + + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/form.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/form.vue new file mode 100644 index 00000000..d5c07c07 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/form.vue @@ -0,0 +1,296 @@ + + + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/order/movement/index.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/order/movement/index.vue new file mode 100644 index 00000000..465fadbd --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/order/movement/index.vue @@ -0,0 +1,7 @@ + + + diff --git a/sql/mysql/wms_moveinv_lock_type.sql b/sql/mysql/wms_moveinv_lock_type.sql new file mode 100644 index 00000000..119453a2 --- /dev/null +++ b/sql/mysql/wms_moveinv_lock_type.sql @@ -0,0 +1,20 @@ +-- 移库仓位锁类型。按字典类型和值幂等插入。 +INSERT INTO system_dict_data + (id, sort, label, value, dict_type, status, color_type, css_class, + remark, creator, create_time, updater, update_time, deleted) +SELECT 2080566516439883781, 4, '移出锁', '4', 'wms_lock_type', 0, 'warning', '', + '移库来源仓位锁', '1', NOW(), '1', NOW(), b'0' + WHERE NOT EXISTS ( + SELECT 1 FROM system_dict_data + WHERE dict_type = 'wms_lock_type' AND value = '4' AND deleted = b'0' + ); + +INSERT INTO system_dict_data + (id, sort, label, value, dict_type, status, color_type, css_class, + remark, creator, create_time, updater, update_time, deleted) +SELECT 2080566516439883782, 5, '移入锁', '5', 'wms_lock_type', 0, 'processing', '', + '移库目标仓位锁', '1', NOW(), '1', NOW(), b'0' + WHERE NOT EXISTS ( + SELECT 1 FROM system_dict_data + WHERE dict_type = 'wms_lock_type' AND value = '5' AND deleted = b'0' + ); diff --git a/sql/mysql/wms_moveinv_menu.sql b/sql/mysql/wms_moveinv_menu.sql new file mode 100644 index 00000000..834ea780 --- /dev/null +++ b/sql/mysql/wms_moveinv_menu.sql @@ -0,0 +1,85 @@ +-- 移库管理菜单及权限。父菜单按“仓储业务”路径查找,脚本可重复执行。 +SET @wms_business_menu_id = ( + SELECT id + FROM system_menu + WHERE path = 'wms-business' + AND type = 1 + AND deleted = b'0' + LIMIT 1 +); + +INSERT INTO system_menu + (id, name, permission, type, sort, parent_id, path, icon, component, + component_name, status, visible, keep_alive, always_show, + creator, create_time, updater, update_time, deleted) +SELECT 6470, '移库管理', '', 2, 1, @wms_business_menu_id, 'move-inv', 'ep:sort', + 'wms/moveinv/index', 'WmsMoveInv', 0, b'1', b'1', b'1', + '1', NOW(), '1', NOW(), b'0' + WHERE @wms_business_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM system_menu + WHERE path = 'move-inv' + AND parent_id = @wms_business_menu_id + AND deleted = b'0' + ); + +SET @move_inv_menu_id = ( + SELECT id + FROM system_menu + WHERE path = 'move-inv' + AND parent_id = @wms_business_menu_id + AND deleted = b'0' + LIMIT 1 +); + +INSERT INTO system_menu + (id, name, permission, type, sort, parent_id, path, icon, component, + component_name, status, visible, keep_alive, always_show, + creator, create_time, updater, update_time, deleted) +SELECT 6471, '移库单查询', 'wms:movement-order:query', 3, 1, @move_inv_menu_id, + '', '#', NULL, NULL, 0, b'1', b'1', b'1', + '1', NOW(), '1', NOW(), b'0' + WHERE @move_inv_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM system_menu + WHERE permission = 'wms:movement-order:query' AND deleted = b'0' + ); + +INSERT INTO system_menu + (id, name, permission, type, sort, parent_id, path, icon, component, + component_name, status, visible, keep_alive, always_show, + creator, create_time, updater, update_time, deleted) +SELECT 6472, '移库单创建', 'wms:movement-order:create', 3, 2, @move_inv_menu_id, + '', '#', NULL, NULL, 0, b'1', b'1', b'1', + '1', NOW(), '1', NOW(), b'0' + WHERE @move_inv_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM system_menu + WHERE permission = 'wms:movement-order:create' AND deleted = b'0' + ); + +INSERT INTO system_menu + (id, name, permission, type, sort, parent_id, path, icon, component, + component_name, status, visible, keep_alive, always_show, + creator, create_time, updater, update_time, deleted) +SELECT 6473, '移库单更新', 'wms:movement-order:update', 3, 3, @move_inv_menu_id, + '', '#', NULL, NULL, 0, b'1', b'1', b'1', + '1', NOW(), '1', NOW(), b'0' + WHERE @move_inv_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM system_menu + WHERE permission = 'wms:movement-order:update' AND deleted = b'0' + ); + +INSERT INTO system_menu + (id, name, permission, type, sort, parent_id, path, icon, component, + component_name, status, visible, keep_alive, always_show, + creator, create_time, updater, update_time, deleted) +SELECT 6474, '移库单删除', 'wms:movement-order:delete', 3, 4, @move_inv_menu_id, + '', '#', NULL, NULL, 0, b'1', b'1', b'1', + '1', NOW(), '1', NOW(), b'0' + WHERE @move_inv_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM system_menu + WHERE permission = 'wms:movement-order:delete' AND deleted = b'0' + ); diff --git a/sql/mysql/wms_one_click_set_test_data.sql b/sql/mysql/wms_one_click_set_test_data.sql new file mode 100644 index 00000000..6500446b --- /dev/null +++ b/sql/mysql/wms_one_click_set_test_data.sql @@ -0,0 +1,90 @@ +-- 出库一键设置测试数据。 +-- 仓库:遂宁成品仓;库区:主存区。 +-- 所有编码均使用 TEST-OCS- 前缀,脚本使用 INSERT IGNORE,可重复执行且不会覆盖测试中的锁状态。 + +INSERT IGNORE INTO wms_structattr + (struct_id, struct_code, struct_name, sect_id, sect_code, sect_name, + stor_id, stor_code, stor_name, is_tempstruct, row_num, col_num, layer_num, + block_num, placement_type, storagevehicle_code, storagevehicle_qty, + lock_type, creator, updater, deleted, is_used) +VALUES + -- 左侧出库:从小列号侧出库,目标箱前有一个挡路箱。 + ('2080900000000001001', 'TEST-OCS-L-01', '左侧挡路仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 91, 1, 1, 91, '02', + 'TEST-OCS-L-BLOCK', 1, '1', '1', '1', '0', 1), + ('2080900000000001002', 'TEST-OCS-L-02', '左侧目标仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 91, 2, 1, 91, '02', + 'TEST-OCS-L-TARGET', 1, '1', '1', '1', '0', 1), + + -- 右侧出库:从大列号侧出库,目标箱前有一个挡路箱。 + ('2080900000000001101', 'TEST-OCS-R-01', '右侧目标仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 92, 1, 1, 92, '03', + 'TEST-OCS-R-TARGET', 1, '1', '1', '1', '0', 1), + ('2080900000000001102', 'TEST-OCS-R-02', '右侧挡路仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 92, 2, 1, 92, '03', + 'TEST-OCS-R-BLOCK', 1, '1', '1', '1', '0', 1), + + -- 双向出库:目标箱两侧均有挡路箱,左右移动数相同时默认选择左侧。 + ('2080900000000001201', 'TEST-OCS-D-01', '双向左挡路仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 93, 1, 1, 93, '01', + 'TEST-OCS-D-LBLOCK', 1, '1', '1', '1', '0', 1), + ('2080900000000001202', 'TEST-OCS-D-02', '双向目标仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 93, 2, 1, 93, '01', + 'TEST-OCS-D-TARGET', 1, '1', '1', '1', '0', 1), + ('2080900000000001203', 'TEST-OCS-D-03', '双向右挡路仓位', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 93, 3, 1, 93, '01', + 'TEST-OCS-D-RBLOCK', 1, '1', '1', '1', '0', 1), + + -- 空闲移库目的仓位。 + ('2080900000000001301', 'TEST-OCS-FREE-01', '移库空位01', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 1, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1), + ('2080900000000001302', 'TEST-OCS-FREE-02', '移库空位02', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 2, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1), + ('2080900000000001303', 'TEST-OCS-FREE-03', '移库空位03', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 3, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1), + ('2080900000000001304', 'TEST-OCS-FREE-04', '移库空位04', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 4, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1), + ('2080900000000001305', 'TEST-OCS-FREE-05', '移库空位05', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 5, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1), + ('2080900000000001306', 'TEST-OCS-FREE-06', '移库空位06', '2078406241102450689', 'ZC01', '主存区', + '2078405279260463106', 'AC01', '遂宁成品仓', '0', 94, 6, 1, 94, '01', + NULL, 0, '1', '1', '1', '0', 1); + +INSERT IGNORE INTO wms_group_plate + (group_id, vehicle_code, status, material_id, pcsn, qty, frozen_qty, + qty_unit_id, qty_unit_name, remark, ext_code, ext_type, ext_dtl_code, + material_code, creator, updater, deleted) +VALUES + -- 挡路库存统一使用测试物料 B,避免被目标出库明细误分配。 + (2080900000000002001, 'TEST-OCS-L-BLOCK', '02', '2080700000000000102', + 'OCS-L-BLOCK-PCSN', 50.000, 0.000, 'KG', '千克', '左侧出库挡路箱', + 'OCS-BLOCK-SOURCE', 'TEST', 'OCS-L-BLOCK-DTL', 'TEST-MAT-002', '1', '1', b'0'), + (2080900000000002002, 'TEST-OCS-L-TARGET', '02', '2080700000000000101', + 'OCS-L-TARGET-PCSN', 50.000, 0.000, 'KG', '千克', '左侧出库目标箱', + 'OCS-L-SOURCE', 'TEST', 'OCS-L-TARGET-DTL', 'TEST-MAT-001', '1', '1', b'0'), + + (2080900000000002101, 'TEST-OCS-R-TARGET', '02', '2080700000000000101', + 'OCS-R-TARGET-PCSN', 50.000, 0.000, 'KG', '千克', '右侧出库目标箱', + 'OCS-R-SOURCE', 'TEST', 'OCS-R-TARGET-DTL', 'TEST-MAT-001', '1', '1', b'0'), + (2080900000000002102, 'TEST-OCS-R-BLOCK', '02', '2080700000000000102', + 'OCS-R-BLOCK-PCSN', 50.000, 0.000, 'KG', '千克', '右侧出库挡路箱', + 'OCS-BLOCK-SOURCE', 'TEST', 'OCS-R-BLOCK-DTL', 'TEST-MAT-002', '1', '1', b'0'), + + (2080900000000002201, 'TEST-OCS-D-LBLOCK', '02', '2080700000000000102', + 'OCS-D-LBLOCK-PCSN', 50.000, 0.000, 'KG', '千克', '双向出库左挡路箱', + 'OCS-BLOCK-SOURCE', 'TEST', 'OCS-D-LBLOCK-DTL', 'TEST-MAT-002', '1', '1', b'0'), + (2080900000000002202, 'TEST-OCS-D-TARGET', '02', '2080700000000000101', + 'OCS-D-TARGET-PCSN', 50.000, 0.000, 'KG', '千克', '双向出库目标箱', + 'OCS-D-SOURCE', 'TEST', 'OCS-D-TARGET-DTL', 'TEST-MAT-001', '1', '1', b'0'), + (2080900000000002203, 'TEST-OCS-D-RBLOCK', '02', '2080700000000000102', + 'OCS-D-RBLOCK-PCSN', 50.000, 0.000, 'KG', '千克', '双向出库右挡路箱', + 'OCS-BLOCK-SOURCE', 'TEST', 'OCS-D-RBLOCK-DTL', 'TEST-MAT-002', '1', '1', b'0'); + +-- 如需清理,请先删除引用这些载具的测试出库分配/任务,再执行: +-- DELETE FROM wms_group_plate WHERE vehicle_code LIKE 'TEST-OCS-%'; +-- DELETE FROM wms_structattr WHERE struct_code LIKE 'TEST-OCS-%';

+_ij4@Msy zoqmq~GFr#)48vc*MnrbH&dy&eGZV$MCeV1I3STEpn(!9=N2@7EXD9tcj0~nUW*lV3 zBvfcgJk})^MxWZhOTs=H3>rqB%Ko7li9GEOpy@V6Di^KAip*%bpV~Yj#Hn=<)fz;o z%rV+!kPgh0utJ~NR>Hk_T=O2WzR8mwcmD@hoDR4{4p#dv{H*ggLgFa_hJ%*~O&wT3MF$Y+`Uk^s2=bA%c?<6F*C4dT*C8G`;&je;@qAvxMNALe*^(0eqk{bAM6G zM0y_7dW*6Nb6Is)R@y!M%TXc4aTmiMNev+VFMvrRsV%X)LXJ}Ue2~@_+U7}kDWz|2 z9s+OX{`JL<;S@In;L8B?m_{(jw8+^l?r{oz`Ob_YPI| z1D|KBrbV-zlZhU>OV}<^d$rT|xFj4z3Do7Yr4c`9FpZki3DRv7pr4d&qlq)l#84qD z=IyNDj_0*bkdv=9KMMKLKRX*is+{La@e|az(wwgh#r(C{|Dy-*5Mat*Rh39JF22*KRJlL}tR}(Y z8DIziPzgj`A4y>jR&a$8;Y}g2xjc$Xr7wcfZllWPG&f0sfI#Aw{18A;NP+<`$a6pS z^y6q1K-U1aU9L%q7yuVw#IY_QJS@#!F5ywF4K2tn+zcek1G|FA!IgZr5>nlV9P0%$ z9d{Xh_8Emc3ne$YKa>Ky7WhAoz-_PXp^$|j4A6iM7mgL#>}VUFhvAYw&oY+4?FZdG z&@R)5il_2LP^L5-B<}ze>v6IT6$^mJuq%=CJAQ_Z2&Rf5ISrWfb~_C;-$1cl=$u2o z0=(8_GSR$1AE*`S8hd+FYQ%Y|c-qSR2a--3uT3pju{(umr5+UTwp^e#xjf7fLDxpl z0J0SddDvl)l@Ky28-h7$BL!i)T-|;b3%n;ZGK3c=J0Sl62@i6xJir)mNE>N@jyxEd zhXZiXoYh|-{Xhhaqez6*zQdqac;zENK1CC?J7NztkQAJ;(R4^B1e~$5oaZ;CE+)V1 zzMahM_$WLpeb0C+ni^|6OIk65MkCZvMsbBVdQoCDDZgs*O97z{eMhzI8FzK3lYNYA z^2xT-$`M~)U8fBFP66FMO-GPDd7^iWdYrLal>8uJ`F)~?mZ*Q^P8^=P*9^Q!xW(Rt z`{Bad5~qoEMri0oz2KF{b>HW|tE)~RpZ5Z5-!Rf33pmu~i_|l@8FOj4c_T-%9VIXj zR0X{_YZiAY-D|P^7a%hA7u&TUSJvPmS~(uRQ^X z|JMRAO{`rAiii{iw4ocn(7+4Fw{SES2M?fHvG(9@|4&KDz?e{*A#65!?%AbWA9y^4=HR$MG9y5V;*mI zht|lCsb=f4 zJ-G|uN}lE4*2wd0`1zAsBq>%aPx?NZagWZ;%YYT*l>;|G56oO$tIS(-p>j(W@=#4Z zZDow^t&LcsQz;q5D+zydGB|fub^8Fou-LxUnBDC+m3Kk5R^xeAGV6KJlU36VT|qYF z)D$Q)uP+2qjl$XP2z+Bb5GLpA3O@OuSI$>vHnX=^LL%l|0s;l_n~X~}0K#0i(7Vu7 zs&_Lmconh4l+?2Unt*dyhl!5(aHDFj|Jqzj!kvudpH}6Mij}4B>FZ<6)NV~bgZ=7l z>iKXEC;T*E$2Cr7J&GvZ*B*yQMyheS#&;VdpJ3;jZsTQlV;$lIqaNXWSTT8FATUu` z_~XagR^{BNFDhnqR4{*a-_uxGnT}TV%X;@sMCIXr_cI8ZJ&%LvHVv<3TB?ENWh0J! z1?|`wJ94ibkGUbJrhkC;K%VOCx0E9ojyB`H*W)>bg$v;`DCk}9$FK0dAyl>_mx8@_ zNUiZ)B|I!_4V;CZ!yn$}O_x1-gtl5)%^oGb4rb`@FUtREien zZm#B>_7)|;z%378Wv(z#nigheGH0H;P;b{bu15pTlBK5Sr2_v+NpRCU>V$pS3$Tu+l`Htbq(rqWbS(>o5Z_ z;e2cE+G;CLIVx38y?=N2Es^!}9w+9=zPLiY)OV(+;Sph7b}Y%aEiXSG_8rEM`QU$( z_17J~B_`96vnI@VuAOMQt+@AVn^Yc!gmsTk@=%QgLsLUzWU;x9fiZ{kOJC)W+IK7s zEBYi1v~+wCnUF~A1*GKYL7zwwXz0TuBFr5fmGw!?-P|;Itf!#|333E=>c(CNmBOUt zV)Iinm@1rztOc%igG7d*AYQ4l=CD^LK&t_G1=`;)-7B0N92pA>3;7}Wc9=N|UK{WD zU3cFPe)k04TBpHN134PkadVUnhBg>!{K)Xbz7zVo>oldkqn%61O(uT)7!uPXVvpst9w7IF;3*-tncVjM7T(+awiuu<-S0zZQ-pi8a;4&ns6-lZy=n^ zK~qN~YV{0spYIsrRn+l0`MLA`hcKP4uk{AW)C$+;p6U)aU;6dVdR2n3U}&<@$7dz6 zN#U@IrDzSNSNFetnzItCf}WiL3D1QcR5!#zs@TsScGJ()IBDIS=v|4N|2okdXt^tt z1?dgyCcU%SBe5#6U5ab7C!RgeSl4=Z=7#U+U4=Z@`LpTJcs1p|yb#1C$erJKG1WK% zNZks5g!g8p_iM&C-}=q%RAA^}M#IgO8YjH7Q3Lnnsd&?%X52tj&W^GadFlnM_)(s% z(YkNDq{eKzF+WgqI%r8kZbYZ!;}yLVFyuyHr!Lb|xUq14Kox2Q*cxZ^nYoKKB$=%@ z!=`G@LoG&xpZ0RJNjzJ$*jM5-IQsotTIrjkn;TyqKUHN*+ty*W(vwGLTAD21E5Ow5 zmXZUQKdeu>HrvIDN>@F~hm8gGv-ciWKY5U8kN}7^+}PKY_w^oPwY-H=NWMfXI@(w! zXm)AoYjt(C5g$Fhgdz|SF$T_ihc>fyc>@CjfmQ+QZYW6r&<%U+)aQV`2v)nz`Xm)f zR{4@L^LBzMotLfGn*tW}9!g=>oSvzSLd0&0_e9E)xuub~Azm{#5?JEm;>v!No(4oj zmAQJ2J9mNm*Pai^FM@`O9a{maItJ`mDj|2}NTK5dNQiYB({Z668PypB0%#B*1X4;A zSxuLwm6}527Pt~o&_-tH2&SiGekWO}NJ<20h>4L=mc&BlsI=P813|qO+7?pME;UjS zPdt@Z2@!T*sJbkeMCcxMI%%KrsR3#c9LVP)6s{1)l4KXVcO^Sgtg^z4*)XBaoo8ET zW1jkoEZ|{gXsJak)snwCnIv({py(&Z`RCTtJh7+)uDL=_G`ag?;^L)@5z3TLEZ}5? za|23B<{w7HfdeIBxRJ`Q4)e{e^wnw7u0W{+S9G+N8ENk9%#B^uG1urgKXc**LacS@ zF2-Q{;4i6z9{?20uwkUocR)n8W@^>o0U_Mww9CCdd;t7I=C0uG%Nfa=!{9UPsjklG zRks>A3u8Nt9N|_;EDU7RBmWHCi;>^IKSKjp4!kvmfHQF9Zy+Xf4DO*L_=k|kB+v__ zj~>Y|@3)7OgXW_1Gh4fOEZx$bt>O;_+O>sA$~gkwmsQZp8HWwP9CT!12ldN3=#wTZ zZS;uJ5Md1vj*B7woNN};&;mPCXF`fxOrc?D$AEDg+DEnu7r1X#hwrO!FL*Oyh}v*?Uz57Ojr`K=Mo2%H?zx; zS`NmN@$PMTt+!Vu7xwjfQid6k1~HR&4D9ZHhLGF*5d)b~$s(Sl&aOW3*rx96k}gpV zliqJKx5-!1ekc(zvBs9Wa7>PBTEy;oAy28_3$*+$eSBMaj^|+&eM4QV4w_FM82i8d z8dfB9-ve1`Se(BcehfUTG!*G$1GG|HP;}g9e6!|zu@fJUPc756$^n1z`&iGX!pJ5|4)-_V>oz8F_zRYdh2CPmtC>?FvJrpY1GWR z7a+t3u~Sx&wF5i>W+I##ht&w}G5HF+9EH?Ta3jUM(XwlChIfB_JOSA4vY$WG1YEZ1 zXx~E(>%s4N`m;AhC@Jjiy`6(1?dlBRl__BDw38%@_~^o&Y_JAjGv_~#luFeo71=;y zuJ$zuwn_Z|NRMe=a*zn`#jSs&qz8I!9Mg zbe+^^5!UUTbzXewF^cUJ&&>Yfrx@E!ub}~TfVrioTtS;kbxHv>R&3u2ryvLGT;FfI zEag$GH37Szh&1Pf{e1l7*d6Wj;L$=aMjqL(oa2rOu74>E!t$nsDFV|$NS6)BIR&rn z%TdjlDm$^#(o$FlseneomFQIR+tBWPwuCMZ#wHEI712ppF#?aTsjE8*&kn3J8b@`W z!l|DaHv+^x=f(i50|~&Fo)5^C3me$hM5z>fyE`PC$Mh~C-#K7uoGwr8WCo7>UCVg; zPGa6JNoRMZl?{bThmTs-|GbbK3V!u3!thJyDTf`_+)0e#VwI`f6*i$arn8vS>PxNi z&|E=-lZWPaU?`~hScFu(wuVM9zqTq)WoyTX-Qx(z{k6)w?V&l%K#J+Pm|GCk6PjV;J8si*ROn^d=8gmkzi!+o%7xY@*$v$dDAy-?Lo8Z@2&DO^)P>ErpcW zS)H)`4Qk?7;oKK4sUtHcy{5c+B1NS(=CUhFGL_bIqX0_A_DM|@UtQcaH8K(hoEy0s zJ^NyI-1xoD%Ve{elznX7H8H>6{e5uRwH82++;tA~YBNrn4!(37adLA^9v}UTI@L7S z{+yv|Rs7ABsJrUQGc&?;59Z&E_@Zt~6(P8uKTi)14zB4QU#t0`nR(C~j^j^a7#-mf z?wS%`CKJeor&f+Gzv$42Fe+O-p10&q3VSd10So2h=*A$_Sku#g=s`SQ- z+Qy-xtH~nzht*4GHpX*v8%us4jz{1eJqI-W2cRN7fBvjN1JC>*^t|upQUTfRA}bi? zyA&ec+O)Ka#GA;z_bn6f1(cIN2fojiv=KXf)Z8<1abcASGv*r}0c4YJ23?4m?K|3@7b$N^S#}WUkM7j?ZA@^=9J+knS1K->f^) zYI3ryi<7B6DOj&!HJd^AKYhNAj~UMFo5^FP8q9cAKhf!u`;K^aDD}+xX4-niwcy=U zv1BYvt(C3JX!a0%OvcR2G4HmoE%nAhBCsIt3C{lHaGSW6*-0=1X`p^~a_j5Ah6p0G zSm$VYUDOe>fkx(z+tJ*D96yvt3vp4Srt1^~PI$2<*4YjvX;W}B`oD`2_B1l?O(-cZ z2o10OmUaqoxld(cEl6R}lGrDitv@-MM?gMTZ!ME$R5W6g?T*sp@XZm0SJ*q&(zLv! zCciBpAa~F_Ud1)?$X;qLt`gg3iBF%ToLfcBH8mz`pfzPLPWv4vgYdIFp9sa7^^EH? z%PR-{Cs$@Aqs+L>zm5-Oe%Gv8dp?hcvcJahdzuz=T@KY8+v19KnOyRQ>QKnpc3HDW zPU{#Iy$W$)-bodrLUh!^MgAtG!AIcY$cdKIda$rDmImMmTExT1^!`W6u9!dHDZAPT zlxDMcs?H^PPo=r6Y|5~`bG%npbE{R8_I}}vYV84DGyBzI+Cxuh zgmZP~WE+jw^-o6Q*xs6E7MXZ1WRQP~J!{2W+I&VbT*yGGkAfSmx>EqP*fvxe0Okx^ zO+TV|G;&`-FzOTMlCkOc;&jD;<*kM|gfI_aBye8J#6Fe*-~f(#PbHzJl-BHp4E!7% z(aMj*rP3yto3UP&J9B%!D=;_Nu0PR5zNtLkOFYUKd8dQ?F5^P2Z^e*n#+mt$04~4QOx7)o+WsiRTCR@`mp{#=wq12jIL(!o;>6Jfa z(Ir})Ka-=FfI&i@w`E5Do>S5(5})U)@GW}zR2yP&q!RILW9T2z*i22pmbp@!(7JTg zG_4_bDEgC;+@4juaiNi!)v<#%&rkYK|zdV`<`IcUh59=_BTxUYMZt=vDD2v zW|cQf9R=!{2ZihPwHFmsFyHQfcls|Q^t7P}AW){%zA*&WyoqNruidMZhZK{RIrlOK z8q68P?00a;)dZL{(CLw zn$j1suGwbOg(dHf^80dFqHpKkQ(Lok%&HXaXDeK@A`bdJSr#^)&d<^>J8DULXiYbv z`eW^I?biAfw;Rq!oyzqEP5*19@#-F&ILGrfZoYPFQG?R2LwdMP7QfN7JwV?3>=m@e>58;CHQ~ zrVE_I8p$b?25%B^#CR%CV*4A(TbL*)TSE;^9(Rr}?Cg?GO*nB)Lm48(t$vzasB}Ie z!dcUZee2;l_#nvYc@_5$%b`{iLzc*yH-@4$QR%vOFWz{05BV-2q@Q{4fBTAWH`>rJ zxju$J6yJbnD!FCy+i8Z|Gme`yr&>g~a<5?RxZc!VfYil$+%zlnjg=Z)FzDKwb@#Ym zc>Rl}#70GNk1~`S+Yzbpxr=!ihv#pRxAY>&08IyRBu@}~d|vg#!FXn;-83Ui9p_(R zm3+fMZgYD+#~>z#%rJu`vwy3&l^MgtL(Z{latoK*Hw9Bmn{3a4)wMY+u~J|ZdP1i1;(gHE zx1Kjj86hRLB%8QbTd>rlZYgUm%pBA`@D7smpSw^fB3~I%4UdqD z$gS)Zn6V{SbBJvDw1h&~3XVlpk}lr7{#vVT%lxw_v5be! zn&{3^I<eLJnr8YwhB|YDBu9-cq zxBK$+?6QpI<=3!RZ=cmX^hq2l8c>bP=e}L_Yu}ppZknEQ!~NK$=N5=&>J79_H)~^` z{>cMhYyQeL#-!NbX-gJs`f6PP9Kv!n$LU47Mw>%km0pkN)e*t(vmOaVyHQ5wg%&wf zr6YY#^&N>-IWpN474CiI^={IedaniRXEj^a$}G8G4=|#QO||?-aW1`uw_fe@M1OKTVrE(;zQvC0pX`YnZ1 z(~{CNyoPAW*ihpVd%58O`Z860CUjAaYYfW!jR;_5-&D_z$bzmM`Y*Ehq=!7 z^h(j3uj+3R?VtT zF%#TSB*&xM&zvg7BhXSR$h>S;kJ;<#wvr;FMrHL-QK|8Nw)y^K$2Fo`Ip8z)6fsN6 zo1P)oyBkqIO46p{>cUUa=^&(`rCMCYJZ862Rrt-7E|VeBElE$p-|;WHV`$frvepyf zdH#~od~Lq#6LP5uImtKj>dan(kBA>n)rG!xMEqyv@&Xc&pXnBij^|G?FrMUBX%6r2 zmk7?q7=EJ@C)vU@m%J@4iE37swN-#iAurqz@n51|^Y4VoJ8BF^rN8Va5-V=-R&O1~ z)BU3iWF`*9vC177xM%#r`|zV{$Kx1BjN;fUnd!JDo~(mJ9Zc?{q$mLX)9tH&Ou1~E zdV1EG9MAVYPw3K-AD}!>f+e; zgQ{XW2*XOR=nvfYY_t|D+={-FZ+ds`MCEC`%Kshm%Whli_bcCBX?9i)Y;NR?eV$ch zeE|P!`NDgX1$|szc*t{Epvy@IT{I2YF2Fg?8X61teJVHp8lxscBdiR)Fb4!NbpQ%-S@8aqM(cuhv#AR{v728nI+6h;mq6b>K!4h|}^G;;;4v6~ub` zYbquu*LLHMasLTdAnI+VH0f6ziPguPmU$(!4W6%~STR$iZ4fi1>7cDW+vC#iJAj@# z+K+ngIBh@I=m%Qf-^oGMzXK)N5-b`l8ivd6)`QsFy_cR_nWI~n8Qui7KUI@vW-OiF zM(d6gN)ss3N_Va{Iq4XCrsC|Nyd8++B)b@!C7?p%C0>pFSy1h|m%W~%6bUzfCJ0T8C#|dbz$?u(cyTD_`vd}%F zIqQhNeCm=m)*8O5#2EsyxDe6umOIJHE6pG?j9f1@qIeW~TDsko?aK|fl#sRz0F~g? z{7^Gfc7sx*(k=e}HSfX&V>*UJnU#N>kP}ebqX~5x{(Wqu@ zw=q0OYOLreYu-L`)(K11l+`C#e#-|C6|(F`*)jH#Ds<|@qSwcA*ymNNwV(1)T5`Uk zJC{;fn;`)Z3@A~?3xD2SO3dWJ3p8P}mp;Lf7xvZKgKwyIXa~2@*0(S&f`yed;!{?; z?82<)zN*0*HT*U#HmcVXX}$CUZj zg*pGDCS+qTxT(|wiqUE+ne1USOfKB~ z@5|M1=#VF!RGxnOb0B!ukV^ipgV^iVVXC8DB<~-PUvYeuQ%T1Q=1%mE*X^?tfEm_^ z{N08Lhg-lb?%1Der|I@b?Hc-UwEuR}?mSv1$@Kb~+uXRMEKIe|@YQiZU~sX%I-@_!q zn2^6^wax^m7`^cfd9%s5(iTFSy1>HKXhg7WeU)vb>tz9+ZTilROx)Af5)#4v_V z30fxW&VRU)Cqao=0@xvIDD+mE7!*ga+(B7)9cM~M#R*)pz7|R{FmCjS@Fw4DmD-|k z)EyL*ECC6WHJ_t~gYwE^Arf4>Y@fb^XeY`*{lUli4^r>Ex8*!=CM8;_DQs<`?11^=b&v!p`v>i!GprRb6PQEnO zR(YFW$S&cJgYpl5%!xj@YZmo0P6WzFO|&`RUvJ6G=a^6;ulr4FDI@oz8X6UIg2MBK zP}kVBghrPVe)?EC>5SIu+7w*hMnNILMVq~qM)hy7n$@e-x7~R&<2K$`Q^woQrd*FZ z`~SZdfa_`1?J2cdd4@w_-f=2a6qJ|dRsz(kg!S3~kFT!)i>h109cd|*MnII1ZX_iH zK`H5O5kb0BN(2=Uq(mB|yE_Hx1__A)q;u#2hJm}r^Ph9{-22@9@ZdP?SbOgkU%c=8 z32fh)pI9%eY=JaI?C@8IaEOC*zQBN%-5V@`K&EbUvZtkvOoi{5B;r&WRm`(h&qE+b z*e4sU3m*#H?8&?c+e>!^-t5R;{ak6iclHVO>X;rYVoO&_Y2OyIUI-vZLRuA%+{ zf!Ju;2El55$^!O{f~lr4R6t5h3SKzD_g(!q54X54Wbh218xrjF&RE~S!nvHm&cE8 z!``#1Vx%A&n@7(!)t|`iC zl53EnVME(ph`+4RIkvx*7>&mhyq_8ibNN;g@q@X;;aK?92?OZ<1mEu|oAuZC!VJEk zB}^L5C%oju{2oVGzeT!DR_*@#Y?WARQxJcx=Oy5U`5Q?u&_qqpiK+?=bzViF?RGkp zqeCQ#!*~uF*SQO->v_nt{f%S?sD2;$uV4Oivf!AnCJ4RD@VlUaMMT)M)`6g>{;~NF z2qznX|NB2O2}?(Lp?_@rA8=ux7{Gj7RQwq@+pM4aKzyh?B)|Cy=BteS%8BVyZPsx# z^v>_bzWa07qrYx?Q(Hz{35V00g@IZFK=?_tG3JlcK_J74IE~VeZd$rlf*YecaJ22_IQ^LPaMMb|y2r9Z2 z6&C|^z$+IQ#rF2IXIw-Oh*$|&TO1u#SDbVKSP0}_BG)|2C$wB|(;xkPhL6ID5*Wb4 z*8x@~Dr0hgNxgv*;*Sc=fmOVB#R=Rj5K@VnVErZaAJwsGG`no_vgQ2sL@{&kYHI2YH2xlG?;P5+21DkN8a=JbrF zs;FTW)RCH!9&@FH&S{EWcsjo^Rj^>dvYKZ44f-DZ9BovLcG!(82Yf1kQK%smtTKrNafMO=l4Y7vM1tL;_}?j z@|CAtS48S?GZNvlz7Uql{Q0>pb5Hk1`oa{zZ=hJyztcq^uYzLX(}=h{RU`4&*U=e9 z1wT=^7!XVu1F{D>Aj?wa8t=8fgAPfvzV+g-_v=5gRp08JXD|WMH^-wj%~`Y;3#6n9 zG9a--(3$|KFCjH`61>)jL!jDd41j?)Z}ov;;uh@xKhso6MgV+z zY7!9M)8jZBGh5CYuC(E)Ey|$EA8R4I_3PILYcR2rp@)Zx2U8_Y07!7gb@i2Y0Qt1# zfUV_g#1+WAH0QC%$>%rd>-J3l;IMAlbzARuOnQ+5a;_kWD*~9Tz@kOHW@Lx)MG&C4 zGj=(mlCX=5roiF^@Lf4$P#D1#WMNS`8UQwoK~Yt=H^CJB_a@2Y!oPQ?Yj|n>EWTlI zz4a2^>mZNm0YC+rQ5#+@pc`K7G8&#vDAao$=a<(d^{zxgePBK`5{?E#V(=uOZJj?7 z8R+)1HpZSGz-+Q1_VdO9i_i3+MWdBZn>*a$FJM!LjLcu?*6ZBwK0X@FHd zMrGfao|EDw546Cw#8cyzgr+MjA{aJCvlLJmIKZBC1$x0W;;%g>jqv_%I|Q;F9F7IG z2H*#fg9Q0B?Pgek0q$~baWT6WD7hH7@oL$kWNWkow$27nCa4@@ zOkdy7sn>4Z+7Z=|xc>1}B0xg51zjoo9d-F7VN8W~X!ttIVxs=iCAaHLzNMH3LjW zs&xi-Fueb5mOtToNUJyXhWKksEda*=0k`v?Lq?Mb+Sh3guT+hZ(~~YM2c*Dfkdp;x z0%IZ33-AsrD;qNxLCROmO)&zL*xi~HgFg*Y0NsDmfI|75wKaDk-=b ze&iD48pkT}dMJ)S8Z|`cui#e`LnQ`UD(su9JNi+Xsm4uoW?{g6F9xN5$;ik7){(PE zEqfw(-R;a!|wIGYEIn(8Pl4`4|H%3YZdr-;6JfLd0HOA`g5dJe#Yu zB+h!2j{p`1xRFm$(5g!$!aPhazY(6ZYNUT8Z0R;&zlPlE^t7=M@6TvD+=%dW8LK9N z_$P-BQBOC$GrNKN$5<;hq`A@f1?M~~fO-RfVc*9?U9!Aeqy0=od!W@{8nI?pw_fzAb&(mSu^t)`o(kRpT0hH>FKv`tVtkSQ5`nfr-ZLC?Q6LLQ$!9ir5 z)Ah;C*8tTDP>L!5e$w9`2EYf)^>$7zOG3SChrl2#-eOH0umUcn)on*cl&iajA3m3B zBJuvv(%rIUW^Du?G!Ozw6Fh=5J2d5t*{VT?!+d|fE8W5LpX(C;mwi$x-GvU5Rin@g zn&@?<)}==U*NVxiYAbWyp`K5ofRTQ_H=z{tDD8$sW1b)&PmlsA?Ikv?rKzW!cdvPD z-~raH!36mO8o&fR0(|T(fPM#$8ihCh2mFGwjd-QOkMH%u1h zdi8oJSSqz1seFE@OgRL~605P`yX<@vJeudc5)=k;2E#L7S+L?BPx@uAT}@u8-NfpB z$$R?_<#-wW6AjK-(rK0AJnj9)LjodN-_!JBfXAts_tgimmJxva|4MHBT6G?|>+T8| z<-i8&a$H6D!pd$fap;xZ}z!9Iez`a{?fVfVh)iiK3inm7hP|Z9_bK#CP z^GZ9!3C$RvszwhC^mz*3ICdq%=KL84L6%I`Xd`n$nF_?G)eic?!)DYyOTsrig5249 zT&XEN%cDS`y8@mxcJ%EP(1KvWm!j_>OyoD81;=f}fC2iUSso56FU zT=>$c^%(`l1NfCZ#rqjRBNhoPz4|*oN2(m0ocBw5;6A{ivMvIA!chv-qVc`GQed&w z+MTDYIgX0<{$}!eZ~@46zKeX+DwaXgN1h>!QLcFy(@u+qAUGyMXQkbohUSuYKzg1cwbH zC^)Is#T~oTwObO8!S(#-Mz#>2e1EWY%~bXMWa}eHt2*6vk2BJxZ7NTiV{Xpoa=gGo zU2rZpFuCldz!&@dNvHd%5+s1jB zCyqxTZob+%w<*+~c=uiWMZ@Tp&?UgQLs89#tkcopH%=xQ%y_{&2^gb_KGaSMuzU|V zwG&&JwUbtUEwxi`N92Yw#)oM~$rZK?g|b-5hlMwK*PGNBn~c2CXKq5)!@PFB%@xj{ z+@s>~<|@1)w}qD3)F=+&f9LabO*nk4G&!4G<*_Nk%bW&f^tImIG+7XvNQk!8KdhM9 z&Q!hUYgb}m^4s7~oBSba59{!kln7FUq-on!Htl5DI=1XcTg{Al?>so3Iftmwf_v4R zxn3W4v~)hDM3M$h3Gv_kff?X#lX>zYA=(=JKg}=5wA-OaGEUB23LBC}xh*XcapDST z3h{aBepg8Ri_RqKsNv)!H$@l3gKpb7i>{gulxuyCO$5<&Ux6!KXD&0 zt63f6I)tbw@2G4*ykvk<&cv>@bqx*DISJV7sO^Yw`t>n6S!pBKFFB!K|j8zUYhbj*2JJIjSjnvW%)k>~i@5f_nAaLoCxf5P>nvdHW)zlSO0& z7xwu#w+K0XS}$dLiC;Syi~kCiK=A9`s$;WewpiqUPeyY^KBQ2pBy@rNiDs(v#mdU7 zXT=RXN0VHdwei@8kYNrh>0RUlJ-p!?)n1DAqVqGCo9q89ZZ0`qI}TMHEWevRmo7#M z&8lAcB1-uMMwVoPdbvfT{qZte1r362JQBZmf(&1}l?xp`8~z<7sMklJ!EE`vP5)`x z?_GxV{$;49dH*Zm@~`9m^Luln2NxE|Fc+G1XXQU%LLildlZ(F}971*R?+66gX*d?A z@qZl*NIg*ixU9dxb7qY{&I4j{;%LCh`+M^*e!KwAn)b1t2`_DN_6La^%KbI*XL))dYKVC6N$$?nazpP*?@l`;bI!Clzc>|jU z7@ER_?+hf}tyfP$fabqj1BfrJ-<6Vv_(K7?Dj%4!o<{9fJKDttBdI`k(F(wEK@deB zE|lMhKKBMj_unHSk~Q>u;o)U;&oLnDV!+xN)w7(_(B0zwcd*p9TWTjgIDEVadeVkhy) zcHZ>DhL{xmf_T?IM;If=je&iJTrs}P0>(lV!f;ZFwT<}o-|u1dca(~g5X6xD2V@Wf z>&EFS!)zsuUr|;cpfaS1A4EognoMQAr4kRpGkBIA9p;C{xydDU=200jP#Xv?AK`6aw1b=%_l-~CMT zu<;oeL+pXF=1;y%HrAp44U&pi7V%w`4)*f zAP9L?-+ZycwS|J50sZI2WjE_0>aNJ8LYM`GyHLitD8wZ$*$wAq!ynN?$t~~tcO;dQ6!mzgQzf`-qrHy zj}NgTZ$Kouytd1_qUQTnoHQ_}DCT_NbM?SPi$(zV1K7@hr2_K8a+)4Bd8r%7#k+S3 zAh|%iJJa?YMy70>_eypwVzp*0e>L~Buf)=@cflmnzZ&2czBPgRG>pVz-4lOb$e{U# zbjz*;@Uwwk7~1i5m3)`EmN?&lb*J}W!~={SMlFDd=k3fjU}ZFQH)EJQTy99|eRP+N zt;>-#Mxpmb1>j3*awaNetBNW?0`rVcg$u7I*LYE>QrTEgRm$jZ;FrpkEV?`iK387Z z{+BVHMm*Ya?{{kh^o7x7t9@)z8&BqV@UKVo`**U;g3qvS zj}{Si_W8>o+iw&UroB&I+5w*TCd1t0$oO@<sxvB308=jq z1MAc)cja}>;3PoGM}wwj4--*IQU=%#CU^+^A0q&D4*wG?g@rl!tqQ|D1hd^F$7Tt^#4QL!lvG>! z*<9OuR><(-W-^oG?jPY;NpH`c1cxu@Vuni^`n5C-9ebdi$N$)Sq5_00-^7yAZDBmrocRr^4-3!+5Y3Ya3=%`;g zYH6t+%>a*zJM0c%Yk?>&h=M#^dVIFmE3Dp--&g~{0E5X?X8aHNGsm6e(g_$7IgPkj zj(=I_nA*5%o+{nC{N0EQt0C{Ngl!{oUu|AP|9N?7hQAmBi_UZ-vc93FVM(rOsq;qY z#-N^#IIerwPZ+o2D?j6$+_mA_#5OXugK&kra+7t_2388 zCH~nYpD=a|Tie}WjC~_XIa#E?+-S|#n4_ypN%=FOgR7^vDDN2RaXXP7f^r7aV3pQ3 zT>m~4{OgY;K)s)Tz@-Y_ z)R{Nx*eL?Xh&68f40W0%7mt71;{Q!8QSv?CjUMC+64X9NUy3>_wlpo3=gH*D1vDu1 zJd3HsI=^>}4AiexOZTC}!gj;f2>ZBVOfk>RQJFS@v@NE zm@!~son0%IX_*rlvRc#C0B5ic3;RF@)wr~KJNsw%LtP!>q9y2dbDwcxjxz8g9OF){IXO)*gZ;f(5@)RP&dlA8vR7E}p=r2uH=D2%Ah`2q zs0ekyQPCXkpqq6RrS0U60$+DD*s;!Wm*}7I7_if@_GVV8lDqZ;JMH^biN<`TTYCFJ zmY03g*{uz_E$lCmZR60#SiiHa3AAee7yzv&k?P>sTytw*d24N+hUN7<1+ol_%2VX@ zI-tO1r&h7{3|~}diUq)Jef6%pRw~!nN>(?dAXZ#a4NKg}HZu+fxi1Koat%$N>7K*I zApB-vy74qBDdm5u;r(5_)f8?-Sd)gwmI{$3+v?Jh9aL) zvcPgj$J3Y2RUh*sC!t0qqb@npBQtrpEzz_4oGs9!zM12i#<{cY0i&l)XWBl3dV9~l zi16vck=DQJsH|QdVX4C>kH@UV{x6e1FjMtQ<0eoK+eyao9_TzQI^4_GjX$E@M^{94 zRq>KE&0XwjNyD_FDh5EzZ1FM>bmO}axyO$*f?x_enO+&sPRYJ+4Lm*8Hc7rikv)D7 zCNjpSPmpb&lhYv}4Rc*ug2kCrttNX&Exu!w>pl< z`;?980;hJ<-itx4{|s0EYh$JSxBPKArBUM?3?(kMJWIx|r@lGlFUf6c9@J{j01>L= zEvUt4i#>%`1Uf7o)Mm{&Fv;0H5*!%H$kCNEXFH1>XShF>7wvbnz3--FE~%0j&Qt^st(k8%ArRh(@Q6#Hml2Cu7f~JD>Yi+wCG8C z_izo0N1`GlugTAdY{9t+PhnI}>3_()_p8qs_sw0L|HseXgj_%n2HAv(BWbRJIN$1pMjrs0ZY{;+{Zujd?`CTwWaee9cPM0Jv-d09OqY&`mq(B`i>I%GzC~Y zd$ycA0`@bUVl9?4AIDGDu1Mr_otukXFU9*V+|63w-el&&8^UkjRn5hq+NziNfIhF@ zxOWEqO~=k>eY8Q?}KU}&+X^HJIxUvp{ZQGpWMbsLY?l9na*HpI;L zg+pC`W_KT3YeR+Cl1B+h zRfV>mj>V|eJr-u=A*nnYPLBRDFZN6&kbA25^wH0`Rbxuztxw8dEZY^B7X2Qc<5u+1 zOn*pEl)6drHTNxB|0UHM>7QFP)H@FH1Yy=eBS;=Qn)TPz=lfehu;<1B(691=T_+Ll zMilceWrJqyI=dc}4jy-Es|K^B+%V?WuhQoU=go1TV87R!6{94dNo>&g-N${~+~wEJ zS2(dZ^Sv7GP<*L#qNyuuYY-Zvs!ce$oCxx$GIjeDx8Qe<1(#4U7q$Rerl=ne(N94*rZhm zx~)gJ`AeXMX~p1D@Axs?r8RZZChExhz-hln&p1LG$!crGGd^SG?R2J30zK^BC15!` z!_<7~*f~6&5c?u>%{aB=0oUFKYZOHm&`w!{Z<&4*3*_C~Gu0?9c?UCe5&-qW?+ z^jFK=%;|a5LdWH|rDvC*kY0RHcD4G$tp?O@ZaMUQB+wGR+vMH^>pgn~A(PO;8e)Np zVBU9kiVonmA`r?dO(a^v?i%a7vr|;%U%{E$UG|DsX5Y@F&floYYbcX^WT*AZ%XZHN zCV{v9*Qn#IBc}uc@*KT5S5s-ld${KP{M(}w^J~VVFtv%Z{m`I}*I|=<*OAF`ehbI* z1<=o_%TQz##7cUDs`$&0ni_h>3ug|jng*qe5|8)E9KL#Na{fbSXS5~%8VhY7pBK#M z&gZq5Uoo`6n~7mEzCd02(arOtE6q<4b=7NLT!L&D+jo(ivpCSrdD3OL`=is2g|!tv z?0;VLsjf779Qo(?hFLmiAZ>ipQhGI{30LfH1G$u~@%G%M;F)iEopQfy9l?Ty&?&Dr$73snp)gt6 zRJGUC&==WOu>IjXD|tHUvtw9053{j7Ny7sqf^AdGBc`F(g3U^uimWU6zB0$q%*ugN zqTuKQy}Xy~Ee#wUYmGMq^)j)%u7b`w%m1G3?h*7E^Z1*s_WG z{(`O{rz>wlo7+W=9JI?#OK;sz&(1&n3i*K7F^!p+6HRh;cOj01B4$c3u1hk0LicFF z<_kv@G`T_tZ~BX^ucH0K3Een}WMtNZB5wny2Fg1(NCWd9m{H=(!^1#oei zWK@+j4RH4!Z>|Mx8*FQJ_{IGg}w=IeG≪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%0A%E8{&>VNcS|*dhM@abcRYhZ&E}rI&dED@f%}i^Q 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 B;~4+| literal 0 HcmV?d00001 diff --git a/docs/superpowers/plans/2026-07-27-wms-move-inventory.md b/docs/superpowers/plans/2026-07-27-wms-move-inventory.md new file mode 100644 index 00000000..8f120ce1 --- /dev/null +++ b/docs/superpowers/plans/2026-07-27-wms-move-inventory.md @@ -0,0 +1,86 @@ +# WMS 移库管理功能实现计划 + +> **面向 AI 代理的工作者:** 在当前会话内按任务顺序执行,使用 TDD 验证核心找位和锁定行为。 + +**目标:** 实现移库单 CRUD、库存与目标仓位选择、仓位锁定,以及按来源载具自动找位建单的内部 API。 + +**架构:** WMS Server 新增独立 `moveinv` 业务包,主 Service 维护单据事务,`MoveTargetStructSelector` 专门负责目标仓位选择。数据库查询集中在 Mapper XML;跨模块自动建单契约放在 WMS API。前端沿用出库页面的 Vben 表格、抽屉和弹窗模式。 + +**技术栈:** Java 17、Spring Boot、MyBatis/MyBatis-Plus、OpenFeign、Vue 3、TypeScript、Ant Design Vue。 + +--- + +### 任务 1:数据模型和 Mapper + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinv/MoveInvDO.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinvdtl/MoveInvDtlDO.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinv/MoveInvMapper.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinvdtl/MoveInvDtlMapper.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinv/MoveInvMapper.xml` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinvdtl/MoveInvDtlMapper.xml` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml` + +- [ ] 映射两张移库表全部字段。 +- [ ] 增加分页、详情、按载具查询来源库存、同层空闲目标仓位查询。 +- [ ] 增加移出锁、移入锁和按移库单释放锁的条件更新。 + +### 任务 2:目标仓位选择器 + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelector.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelectorTest.java` + +- [ ] 先写测试,覆盖同层、距离排序和批次目标去重。 +- [ ] 运行测试确认因选择器不存在而失败。 +- [ ] 实现最小选择算法并运行测试通过。 + +### 任务 3:后端业务和管理端接口 + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/*.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/MoveInvController.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvService.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvServiceImpl.java` +- 修改:`nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java` + +- [ ] 实现分页、详情、可用库存、可用目标仓位。 +- [ ] 实现新增、修改、删除事务。 +- [ ] 创建时给来源仓位加 `4` 移出锁、目标仓位加 `5` 移入锁。 +- [ ] 修改/删除时只释放当前移库单持有的锁。 + +### 任务 4:自动找位建单 API + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApi.java` +- 创建:`nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateReqDTO.java` +- 创建:`nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateRespDTO.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApiImpl.java` + +- [ ] API 只接收来源载具编码列表。 +- [ ] Service 逐载具选择同库区同层目标仓位,确保目标不重复。 +- [ ] 任意载具不可用或无目标时整单回滚。 + +### 任务 5:前端管理页面 + +**文件:** +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/moveinv/index.ts` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/data.ts` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/index.vue` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/form.vue` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/inventory-select.vue` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/struct-select.vue` +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/moveinv/modules/detail.vue` + +- [ ] 实现列表和生成状态操作限制。 +- [ ] 新增/修改抽屉默认全屏并回填明细。 +- [ ] 每个载具分别选择不同目标仓位。 +- [ ] 实现详情展示。 + +### 任务 6:验证 + +- [ ] 运行后端核心测试。 +- [ ] 运行 WMS reactor 编译。 +- [ ] 运行前端类型检查。 +- [ ] 检查工作区差异,不覆盖既有用户修改。 diff --git a/docs/superpowers/specs/2026-07-27-wms-move-inventory-design.md b/docs/superpowers/specs/2026-07-27-wms-move-inventory-design.md new file mode 100644 index 00000000..9cea1156 --- /dev/null +++ b/docs/superpowers/specs/2026-07-27-wms-move-inventory-design.md @@ -0,0 +1,164 @@ +# WMS 移库管理功能设计 + +## 1. 目标 + +基于现有 `wms_moveinv`、`wms_moveinvdtl` 表实现完整的移库管理功能,包括移库单列表、新增、修改、删除、详情、可用库存选择、目标仓位选择,以及供“一键设置”直接调用的自动建单接口。 + +## 2. 功能范围 + +### 2.1 移库单列表 + +- 分页查询移库单主表,默认按主键倒序。 +- 展示单据编码、单据类型、业务日期、仓库、总数量、明细数、单据状态、生成方式、创建人、创建时间和备注。 +- 单据编码可进入详情页。 +- 生成状态的单据允许修改、删除;其他状态不允许修改、删除。 + +### 2.2 新增和修改 + +- 页面交互与现有出库单新增、修改保持一致。 +- 新增时先选择仓库及库区,再选择该库区内可用库存。 +- 库存按载具选择,每个载具生成一条 `wms_moveinvdtl`。 +- 每条明细必须单独选择一个目标仓位。 +- 同一张移库单内,不允许多条明细选择同一个目标仓位。 +- 修改页面回填主表和全部明细。 +- 修改保存时更新主表,释放旧明细关联的仓位锁,删除旧明细,插入新明细,并按新明细重新加锁。 +- 主表的 `total_qty`、`detail_count` 根据保存的明细汇总生成。 + +### 2.3 可用库存 + +库存来源为组盘数据和载具所在仓位,必须同时满足: + +- 组盘数据未删除,并处于入库可用状态。 +- 载具编码不为空。 +- 载具所在仓位已启用且未删除。 +- 来源仓位未被其他业务锁定。 +- 来源仓位属于当前选择的仓库和库区。 + +选择结果回填载具编码、来源仓位、来源库区、物料编码、批次、数量、计量单位及来源单号。 + +### 2.4 手工目标仓位选择 + +目标仓位必须满足: + +- 与来源仓位属于同一个仓库。 +- 与来源仓位属于同一个库区。 +- 与来源仓位具有相同的 `layer_num`。 +- 仓位已启用且未删除。 +- `lock_type = 0`,即仓位未锁定。 +- `storagevehicle_code` 为空,即仓位上没有载具。 +- 不能是来源仓位。 +- 不能是当前移库单其他明细已经选择的目标仓位。 + +## 3. 自动目标仓位选择器 + +新增独立的 `MoveTargetStructSelector`,集中封装自动找位规则。业务 Service 不直接编写找位排序逻辑,方便后续替换算法。 + +选择器输入: + +- 来源仓位。 +- 当前批次已经选择的目标仓位标识集合。 + +选择器输出: + +- 一个满足条件的目标仓位。 + +默认规则: + +1. 筛选同仓库、同库区、同层的空闲仓位。 +2. 排除来源仓位及当前批次已经选择的目标仓位。 +3. 优先选择与来源仓位 `col_num` 距离较近的仓位。 +4. 距离相同时按 `row_num`、`col_num`、`block_num`、`struct_id` 升序选择。 +5. 找不到目标仓位时抛出业务异常。 + +仓位候选查询 SQL 统一放在 `StrucAttrMapper.xml` 中,选择器只负责候选校验和顺序选择。 + +## 4. 一键设置自动建单接口 + +在 WMS API 模块开放自动创建移库单接口,供出库“一键设置”等跨模块业务调用。 + +请求参数: + +- 来源载具编码列表。 + +处理流程: + +1. 校验载具编码不可重复。 +2. 批量查询每个载具对应的来源仓位和可用库存。 +3. 对每个载具调用 `MoveTargetStructSelector`,自动选择同库区、同层目标仓位。 +4. 本次已选择的目标仓位加入排除集合,确保一车一目标且目标不重复。 +5. 生成一张移库单及对应明细。 +6. 任意载具不存在、库存不可用或找不到目标仓位时,整个事务回滚。 +7. 返回创建成功的移库单标识和单据编码。 + +接口生成方式使用接口创建对应的字典值,初始单据状态为“生成”。 + +## 5. 仓位锁定 + +锁定类型来自字典 `wms_lock_type`: + +- `4`:移出锁。 +- `5`:移入锁。 + +创建成功后: + +- 来源仓位设置 `lock_type = 4`。 +- 目标仓位设置 `lock_type = 5`。 +- 两类锁均写入 `inv_type`、`inv_id`、`inv_code`,关联当前移库单。 + +修改时: + +1. 校验单据处于生成状态。 +2. 仅释放 `inv_id` 等于当前移库单标识的旧来源、目标仓位锁。 +3. 更新主表并重建明细。 +4. 对新来源仓位加移出锁,对新目标仓位加移入锁。 + +删除时: + +- 仅允许删除生成状态单据。 +- 释放当前单据持有的来源和目标仓位锁。 +- 删除主表和明细。 + +建单、修改、删除、加锁和解锁必须处于同一数据库事务。 + +## 6. 数据一致性与并发 + +- 保存前再次在数据库中校验来源仓位和目标仓位状态,不能只依赖页面查询结果。 +- 仓位加锁更新必须带上当前未锁定条件,受影响行数不为 1 时视为仓位已被占用并回滚。 +- 同一载具不能在一张单据中重复出现。 +- 同一目标仓位不能在一张单据中重复出现。 +- 主表、明细和仓位锁任一步失败,整笔事务回滚。 + +## 7. 前端结构 + +- 新增移库管理 API 文件。 +- 新增移库列表页面。 +- 新增全屏移库新增/修改页面。 +- 新增库存选择弹窗。 +- 新增目标仓位选择弹窗。 +- 新增移库详情页面。 +- 表格列沿用系统全局分隔线、勾选框颜色和操作按钮图标规范。 + +## 8. 错误处理 + +需要提供明确的中文业务异常: + +- 移库单不存在。 +- 当前状态不允许修改或删除。 +- 来源载具不存在或库存不可用。 +- 来源仓位已锁定。 +- 目标仓位已占用或已锁定。 +- 目标仓位与来源仓位不在同一库区或同一层。 +- 目标仓位在当前单据中重复。 +- 未找到同库区、同层的可用目标仓位。 + +## 9. 验证范围 + +- 后端编译通过。 +- 前端类型检查通过。 +- 新增、修改时主表和明细正确保存。 +- 修改时旧锁释放、新锁正确创建。 +- 删除时来源和目标仓位锁正确释放。 +- 自动找位只选择同库区、同层、未锁定、无载具的仓位。 +- 批量自动建单目标仓位不重复。 +- 任意一条自动找位失败时不产生主表、明细或残留锁。 +- 起点锁定类型为 `4`,目的点锁定类型为 `5`。 diff --git a/nl-gateway/src/main/resources/application.yaml b/nl-gateway/src/main/resources/application.yaml index 0aaabc08..525e0055 100644 --- a/nl-gateway/src/main/resources/application.yaml +++ b/nl-gateway/src/main/resources/application.yaml @@ -216,6 +216,13 @@ spring: - Path=/admin-api/wms/** filters: - RewritePath=/admin-api/wms/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs + ## task-server 服务 + - id: task-admin-api # 路由的编号 + uri: grayLb://task-server + predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 + - Path=/admin-api/task/** + filters: + - RewritePath=/admin-api/task/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs ## im-server 服务 - id: im-admin-api # 路由的编号 uri: grayLb://im-server @@ -252,6 +259,9 @@ knife4j: - name: base-server service-name: base-server url: /admin-api/base/v3/api-docs + - name: task-server + service-name: task-server + url: /admin-api/task/v3/api-docs - name: bpm-server service-name: bpm-server url: /admin-api/bpm/v3/api-docs diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java index c9579ac9..7c446a98 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/api/TransportTaskApi.java @@ -60,4 +60,9 @@ public interface TransportTaskApi { @Operation(summary = "根据 materialCode 和 ownerService 查询运行中任务") CommonResult> getRunningTaskByMaterialCode(@RequestParam("materialCode") String materialCode, @RequestParam("ownerService") String ownerService); + + @GetMapping(PREFIX + "/countUnfinishedByDestination") + @Operation(summary = "统计指定目的点的未完成任务数") + CommonResult countUnfinishedByDestination(@RequestParam("pointCode") String pointCode, + @RequestParam("ownerService") String ownerService); } diff --git a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java index 2334120f..d44bcc13 100644 --- a/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java +++ b/nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/dto/TransportTaskCreateReqDTO.java @@ -21,6 +21,12 @@ public class TransportTaskCreateReqDTO { @NotEmpty(message = "业务归属服务不能为空") private String ownerService; + @Schema(description = "业务类型") + private String bizType; + + @Schema(description = "业务侧标识") + private String bizId; + @Schema(description = "业务回调处理器编码", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "业务回调处理器编码不能为空") private String handleCode; diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java index 707e80c1..178eaa32 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/api/TransportTaskApiImpl.java @@ -69,4 +69,9 @@ public class TransportTaskApiImpl implements TransportTaskApi { public CommonResult> getRunningTaskByMaterialCode(String materialCode, String ownerService) { return success(transportTaskService.getRunningTaskInfoByMaterialCode(materialCode, ownerService)); } + + @Override + public CommonResult countUnfinishedByDestination(String pointCode, String ownerService) { + return success(transportTaskService.countUnfinishedByDestination(pointCode, ownerService)); + } } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java index 3028c4f1..b276a568 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/dal/mysql/transporttask/TransportTaskMapper.java @@ -109,6 +109,14 @@ public interface TransportTaskMapper extends BaseMapperX { .orderByDesc(TransportTaskDO::getTaskId)); } + default Long selectUnfinishedCountByDestination(String pointCode, String ownerService) { + return selectCount(new LambdaQueryWrapperX() + .eq(TransportTaskDO::getPointCode2, pointCode) + .eq(TransportTaskDO::getOwnerService, ownerService) + .lt(TransportTaskDO::getTaskStatus, + TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode())); + } + /** * 条件更新任务状态(CAS 抢占) */ diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java index 64e9c9d5..364e791e 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/framework/rpc/config/RpcConfiguration.java @@ -1,11 +1,16 @@ package cn.code.nl.module.task.framework.rpc.config; +import cn.code.nl.module.base.api.classstandard.ClassStandardApi; +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import cn.code.nl.module.infra.api.config.ConfigApi; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; /** * Task 服务 RPC 配置 - * Feign Client 注册已移至框架层 {@link cn.code.nl.framework.execute.config.NlExecuteRpcAutoConfiguration} + * 框架通用 Feign Client 由框架层注册,Task 业务直接依赖的 Client 在此注册。 */ @Configuration(value = "taskRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = {ConfigApi.class, ClassStandardApi.class, CodeGenApi.class}) public class RpcConfiguration { } diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java index 27057427..ddb09fb7 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java @@ -123,6 +123,8 @@ public interface TransportTaskService { */ List getRunningTaskInfoByMaterialCode(String materialCode, String ownerService); + Long countUnfinishedByDestination(String pointCode, String ownerService); + /** * 自动下发待下发任务到 ACS */ diff --git a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java index f752f284..e1db57cf 100644 --- a/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java +++ b/nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java @@ -230,6 +230,11 @@ public class TransportTaskServiceImpl implements TransportTaskService { return TransportTaskConvert.INSTANCE.convertList(tasks); } + @Override + public Long countUnfinishedByDestination(String pointCode, String ownerService) { + return transportTaskMapper.selectUnfinishedCountByDestination(pointCode, ownerService); + } + /** * 查询待下发任务,并委托下发管理器按生产区域下发到 ACS */ diff --git a/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml b/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml index 06832135..8fc3ff85 100644 --- a/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml +++ b/nl-module-task/nl-module-task-server/src/main/resources/application-dev.yaml @@ -7,12 +7,12 @@ spring: username: nacos password: nacos discovery: # 【注册中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 91c8ac41-7fb0-423b-947e-2caad4c87d41 # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【配置中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 91c8ac41-7fb0-423b-947e-2caad4c87d41 # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP --- #################### 数据库相关配置 #################### @@ -57,12 +57,12 @@ spring: primary: master datasource: master: - url: jdbc:mysql://192.168.81.193:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root password: root slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://192.168.81.193:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3306/huachuang_lms_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root password: root @@ -100,11 +100,11 @@ spring: xxl: job: admin: - addresses: http://192.168.10.2:8080/xxl-job-admin # 调度中心部署跟地址 - accessToken: 123456 # 执行器通讯TOKEN + addresses: http://192.168.81.193:8080/xxl-job-admin # 调度中心部署跟地址 + accessToken: default_token # 执行器通讯TOKEN executor: - ip: 192.168.10.2 - port: 9994 + ip: localhost + port: 8995 --- #################### 服务保障相关配置 #################### diff --git a/nl-module-task/nl-module-task-server/src/main/resources/application.yaml b/nl-module-task/nl-module-task-server/src/main/resources/application.yaml index d63db646..ef670d4d 100644 --- a/nl-module-task/nl-module-task-server/src/main/resources/application.yaml +++ b/nl-module-task/nl-module-task-server/src/main/resources/application.yaml @@ -36,7 +36,7 @@ spring: time-to-live: 1h # 设置过期时间为 1 小时 server: - port: 48084 + port: 48086 logging: file: diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApi.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApi.java new file mode 100644 index 00000000..b790e1ce --- /dev/null +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApi.java @@ -0,0 +1,19 @@ +package cn.code.nl.module.wms.api.moveinv; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO; +import cn.code.nl.module.wms.enums.ApiConstants; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(name = ApiConstants.NAME) +public interface MoveInvApi { + + String PREFIX = ApiConstants.PREFIX + "/move-inv"; + + @PostMapping(PREFIX + "/autoCreate") + CommonResult autoCreate(@Valid @RequestBody MoveInvAutoCreateReqDTO reqDTO); +} diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateReqDTO.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateReqDTO.java new file mode 100644 index 00000000..28bf65cd --- /dev/null +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateReqDTO.java @@ -0,0 +1,16 @@ +package cn.code.nl.module.wms.api.moveinv.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * 自动创建移库单请求。 + */ +@Data +public class MoveInvAutoCreateReqDTO { + + @NotEmpty(message = "来源载具不能为空") + private List vehicleCodes; +} diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateRespDTO.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateRespDTO.java new file mode 100644 index 00000000..c0fe5c98 --- /dev/null +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/moveinv/dto/MoveInvAutoCreateRespDTO.java @@ -0,0 +1,17 @@ +package cn.code.nl.module.wms.api.moveinv.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 自动创建移库单结果。 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MoveInvAutoCreateRespDTO { + + private Long moveinvId; + private String billCode; +} diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ApiConstants.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ApiConstants.java new file mode 100644 index 00000000..2d07ceb3 --- /dev/null +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ApiConstants.java @@ -0,0 +1,15 @@ +package cn.code.nl.module.wms.enums; + +import cn.code.nl.framework.common.enums.RpcConstants; + +/** + * WMS API 常量。 + */ +public final class ApiConstants { + + public static final String NAME = "wms-server"; + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/wms"; + + private ApiConstants() { + } +} diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java index 57c3fa7c..ae31ec67 100644 --- a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java @@ -19,6 +19,18 @@ public interface ErrorCodeConstants { ErrorCode IOSTOR_INV_NO_MATCHING_INVENTORY = new ErrorCode(6_000_6, "没有符合当前出库明细的可用库存"); ErrorCode IOSTOR_INV_MANUAL_STATUS_INVALID = new ErrorCode(6_000_7, "只有生成或分配中的明细允许手工分配"); ErrorCode IOSTOR_INV_UPDATE_STATUS_INVALID = new ErrorCode(6_000_8, "只有生成状态的出库单允许修改"); + ErrorCode IOSTOR_INV_NO_PENDING_ALLOCATION = new ErrorCode(6_000_9, "当前明细没有生成状态的分配记录"); + ErrorCode IOSTOR_INV_NO_MOVE_LOCATION = new ErrorCode(6_001_0, "没有可用于移库的空闲仓位"); + + // ================ 移库单相关错误码 ================= + ErrorCode MOVE_INV_NOT_EXISTS = new ErrorCode(6_002_0, "移库单不存在"); + ErrorCode MOVE_INV_STATUS_INVALID = new ErrorCode(6_002_1, "只有生成状态的移库单允许修改或删除"); + ErrorCode MOVE_INV_SOURCE_INVALID = new ErrorCode(6_002_2, "来源载具不存在、库存不可用或仓位已锁定"); + ErrorCode MOVE_INV_TARGET_INVALID = new ErrorCode(6_002_3, "目标仓位不可用或与来源仓位不在同一库区同一层"); + ErrorCode MOVE_INV_TARGET_DUPLICATE = new ErrorCode(6_002_4, "同一移库单的目标仓位不能重复"); + ErrorCode MOVE_INV_NO_TARGET_STRUCT = new ErrorCode(6_002_5, "未找到同库区同层的可用目标仓位"); + ErrorCode MOVE_INV_LOCK_FAILED = new ErrorCode(6_002_6, "移库仓位已被占用,请刷新后重试"); + ErrorCode MOVE_INV_CODE_GENERATE_FAILED = new ErrorCode(6_002_7, "移库单号生成失败"); ErrorCode IOSTORINV_DTL_NOT_EXISTS = new ErrorCode(5, "出入库单明细不存在"); diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/OutboundPointEnum.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/OutboundPointEnum.java new file mode 100644 index 00000000..7f9ad1ab --- /dev/null +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/OutboundPointEnum.java @@ -0,0 +1,21 @@ +package cn.code.nl.module.wms.enums; + +import lombok.Getter; + +/** + * 出库目的点。 + * + * TODO 配置现场实际的两个出库点位编码。 + */ +@Getter +public enum OutboundPointEnum { + + OUTBOUND_POINT_1("CK01"), + OUTBOUND_POINT_2("CK02"); + + private final String pointCode; + + OutboundPointEnum(String pointCode) { + this.pointCode = pointCode; + } +} diff --git a/nl-module-wms/nl-module-wms-server/pom.xml b/nl-module-wms/nl-module-wms-server/pom.xml index 26dc0cdf..dd6e9af2 100644 --- a/nl-module-wms/nl-module-wms-server/pom.xml +++ b/nl-module-wms/nl-module-wms-server/pom.xml @@ -141,6 +141,12 @@ h2 test + + cn.nl.cloud + nl-module-infra-api + 2026.06-SNAPSHOT + compile + diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApiImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApiImpl.java new file mode 100644 index 00000000..c4b3adb8 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/moveinv/MoveInvApiImpl.java @@ -0,0 +1,30 @@ +package cn.code.nl.module.wms.api.moveinv; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO; +import cn.code.nl.module.wms.service.moveinv.MoveInvService; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import static cn.code.nl.framework.common.pojo.CommonResult.success; + +/** + * 移库内部 API 实现。 + */ +@RestController +@Validated +public class MoveInvApiImpl implements MoveInvApi { + + @Resource + private MoveInvService moveInvService; + + /** + * 按来源载具自动创建移库单。 + */ + @Override + public CommonResult autoCreate(MoveInvAutoCreateReqDTO reqDTO) { + return success(moveInvService.autoCreate(reqDTO)); + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java index e7c4a8e5..9e8a8a33 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java @@ -178,6 +178,14 @@ public class IostorInvController { return success(true); } + @PostMapping("/one-click-set") + @Operation(summary = "出库明细一键设置并下发任务") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')") + public CommonResult oneClickSet(@Valid @RequestBody OutboundOneClickSetReqVO reqVO) { + iostorInvService.oneClickSet(reqVO); + return success(true); + } + @GetMapping("/page") @Operation(summary = "获得出入库单主表分页") @TransMethodResult diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/OutboundOneClickSetReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/OutboundOneClickSetReqVO.java new file mode 100644 index 00000000..d6c5ad3e --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/OutboundOneClickSetReqVO.java @@ -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; + +@Schema(description = "管理后台 - 出库明细一键设置 Request VO") +@Data +public class OutboundOneClickSetReqVO { + + @NotNull(message = "出库单标识不能为空") + private Long iostorinvId; + + @NotNull(message = "出库单明细标识不能为空") + private Long iostorinvdtlId; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/MoveInvController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/MoveInvController.java new file mode 100644 index 00000000..8c2ba01f --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/MoveInvController.java @@ -0,0 +1,98 @@ +package cn.code.nl.module.wms.controller.admin.moveinv; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvSaveReqVO; +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; +import cn.code.nl.module.wms.service.moveinv.MoveInvService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import org.dromara.core.trans.anno.TransMethodResult; + +import static cn.code.nl.framework.common.pojo.CommonResult.success; + +/** + * 移库管理接口。 + */ +@Tag(name = "管理后台 - 移库管理") +@RestController +@RequestMapping("/wms/move-inv") +@Validated +public class MoveInvController { + + @Resource + private MoveInvService moveInvService; + + @PostMapping("/create") + @Operation(summary = "创建移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:create')") + public CommonResult create(@Valid @RequestBody MoveInvSaveReqVO reqVO) { + return success(moveInvService.create(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:update')") + public CommonResult update(@Valid @RequestBody MoveInvSaveReqVO reqVO) { + moveInvService.update(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:delete')") + public CommonResult delete(@RequestParam("id") Long id) { + moveInvService.delete(id); + return success(true); + } + + @GetMapping("/get") + @TransMethodResult + @Operation(summary = "查询移库单详情") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult get(@RequestParam("id") Long id) { + return success(moveInvService.get(id)); + } + + @GetMapping("/page") + @TransMethodResult + @Operation(summary = "分页查询移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getPage(@Valid MoveInvPageReqVO reqVO) { + return success(moveInvService.getPage(reqVO)); + } + + @GetMapping("/availableInventory") + @Operation(summary = "查询移库可用库存") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getAvailableInventory( + @RequestParam("storId") String storId, + @RequestParam(value = "sectId", required = false) String sectId) { + return success(moveInvService.getAvailableInventory(storId, sectId)); + } + + @GetMapping("/availableTargetStructs") + @Operation(summary = "查询同库区同层可用目标仓位") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getAvailableTargetStructs( + @RequestParam("sourceStructCode") String sourceStructCode) { + return success(moveInvService.getAvailableTargetStructs(sourceStructCode)); + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvDetailSaveReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvDetailSaveReqVO.java new file mode 100644 index 00000000..533223c7 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvDetailSaveReqVO.java @@ -0,0 +1,29 @@ +package cn.code.nl.module.wms.controller.admin.moveinv.vo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 移库明细保存参数。 + */ +@Data +public class MoveInvDetailSaveReqVO { + + @NotBlank(message = "来源载具不能为空") + private String storagevehicleCode; + @NotBlank(message = "来源仓位不能为空") + private String turnoutStructCode; + @NotBlank(message = "目标仓位不能为空") + private String turninStructCode; + private String turnoutSectCode; + private String turninSectCode; + private String materialCode; + private String pcsn; + private String qtyUnitId; + private String qtyUnitName; + private BigDecimal qty; + private String sourceBillCode; + private String remark; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvInventoryRespVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvInventoryRespVO.java new file mode 100644 index 00000000..427011a4 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvInventoryRespVO.java @@ -0,0 +1,28 @@ +package cn.code.nl.module.wms.controller.admin.moveinv.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 移库可用库存。 + */ +@Data +public class MoveInvInventoryRespVO { + + private String structId; + private String structCode; + private String structName; + private String sectId; + private String sectCode; + private String sectName; + private String storId; + private String storagevehicleCode; + private Integer layerNum; + private String materialCode; + private String pcsn; + private String qtyUnitId; + private String qtyUnitName; + private BigDecimal qty; + private String sourceBillCode; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvPageReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvPageReqVO.java new file mode 100644 index 00000000..8e2a1a84 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvPageReqVO.java @@ -0,0 +1,16 @@ +package cn.code.nl.module.wms.controller.admin.moveinv.vo; + +import cn.code.nl.framework.common.pojo.PageParam; +import lombok.Data; + +/** + * 移库单分页参数。 + */ +@Data +public class MoveInvPageReqVO extends PageParam { + + private String billCode; + private String billType; + private String storId; + private String billStatus; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvRespVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvRespVO.java new file mode 100644 index 00000000..7fbab9c3 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvRespVO.java @@ -0,0 +1,37 @@ +package cn.code.nl.module.wms.controller.admin.moveinv.vo; + +import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO; +import lombok.Data; +import cn.code.nl.module.system.api.user.AdminUserApi; +import org.dromara.core.trans.anno.Trans; +import org.dromara.core.trans.constant.TransType; +import org.dromara.core.trans.vo.VO; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 移库单响应。 + */ +@Data +public class MoveInvRespVO implements VO { + + private Long moveinvId; + private String billCode; + private String billType; + private String bizDate; + private String storId; + private String storName; + private BigDecimal totalQty; + private Integer detailCount; + private String billStatus; + private String remark; + private String createMode; + @Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX, + fields = "nickname", ref = "creatorName") + private String creator; + private String creatorName; + private LocalDateTime createTime; + private List details; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvSaveReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvSaveReqVO.java new file mode 100644 index 00000000..5e603fa4 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/moveinv/vo/MoveInvSaveReqVO.java @@ -0,0 +1,27 @@ +package cn.code.nl.module.wms.controller.admin.moveinv.vo; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * 移库单保存参数。 + */ +@Data +public class MoveInvSaveReqVO { + + private Long moveinvId; + @NotBlank(message = "单据类型不能为空") + private String billType; + @NotBlank(message = "业务日期不能为空") + private String bizDate; + @NotBlank(message = "仓库不能为空") + private String storId; + private String remark; + @Valid + @NotEmpty(message = "移库明细不能为空") + private List details; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinv/MoveInvDO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinv/MoveInvDO.java new file mode 100644 index 00000000..c6ee767f --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinv/MoveInvDO.java @@ -0,0 +1,33 @@ +package cn.code.nl.module.wms.dal.dataobject.moveinv; + +import cn.code.nl.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 移库单主表。 + */ +@TableName("wms_moveinv") +@Data +@EqualsAndHashCode(callSuper = true) +public class MoveInvDO extends BaseDO { + + @TableId + private Long moveinvId; + private String billCode; + private String billType; + private String bizDate; + private String storId; + private BigDecimal totalQty; + private Integer detailCount; + private String billStatus; + private String remark; + private String createMode; + private String confirmor; + private LocalDateTime confirmTime; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinvdtl/MoveInvDtlDO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinvdtl/MoveInvDtlDO.java new file mode 100644 index 00000000..d3e6c62e --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/moveinvdtl/MoveInvDtlDO.java @@ -0,0 +1,35 @@ +package cn.code.nl.module.wms.dal.dataobject.moveinvdtl; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 移库单明细。 + */ +@TableName("wms_moveinvdtl") +@Data +public class MoveInvDtlDO { + + @TableId + private Long moveinvdtlId; + private Long moveinvId; + private Integer seqNo; + private String turnoutSectCode; + private String turnoutStructCode; + private String materialCode; + private String pcsn; + private String qtyUnitId; + private String qtyUnitName; + private BigDecimal qty; + private String turninSectCode; + private String turninStructCode; + private String workStatus; + private Long taskId; + private String storagevehicleCode; + private String isIssued; + private String sourceBillCode; + private String remark; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinv/MoveInvMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinv/MoveInvMapper.java new file mode 100644 index 00000000..c86e05cc --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinv/MoveInvMapper.java @@ -0,0 +1,24 @@ +package cn.code.nl.module.wms.dal.mysql.moveinv; + +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO; +import cn.code.nl.module.wms.dal.dataobject.moveinv.MoveInvDO; +import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 移库单主表 Mapper。 + */ +@Mapper +public interface MoveInvMapper extends BaseMapperX { + + default PageResult selectPage(MoveInvPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(MoveInvDO::getBillCode, reqVO.getBillCode()) + .eqIfPresent(MoveInvDO::getBillType, reqVO.getBillType()) + .eqIfPresent(MoveInvDO::getStorId, reqVO.getStorId()) + .eqIfPresent(MoveInvDO::getBillStatus, reqVO.getBillStatus()) + .orderByDesc(MoveInvDO::getMoveinvId)); + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinvdtl/MoveInvDtlMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinvdtl/MoveInvDtlMapper.java new file mode 100644 index 00000000..33691d73 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/moveinvdtl/MoveInvDtlMapper.java @@ -0,0 +1,19 @@ +package cn.code.nl.module.wms.dal.mysql.moveinvdtl; + +import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; +import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 移库单明细 Mapper。 + */ +@Mapper +public interface MoveInvDtlMapper extends BaseMapperX { + + List selectByMoveinvId(@Param("moveinvId") Long moveinvId); + + int deleteByMoveinvId(@Param("moveinvId") Long moveinvId); +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java index c9309808..9c47471d 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java @@ -6,6 +6,7 @@ import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; @@ -19,6 +20,28 @@ import cn.code.nl.module.wms.controller.admin.structAttr.vo.*; @Mapper public interface StrucAttrMapper extends BaseMapperX { + List selectMoveAvailableInventory( + @Param("storId") String storId, + @Param("sectId") String sectId, + @Param("vehicleCodes") Collection vehicleCodes); + + List selectMoveTargetCandidates( + @Param("storId") String storId, + @Param("sectId") String sectId, + @Param("layerNum") Integer layerNum); + + StrucAttrDO selectByStructCode(@Param("structCode") String structCode); + + int lockForMoveOut(@Param("structCode") String structCode, + @Param("invId") String invId, + @Param("invCode") String invCode); + + int lockForMoveIn(@Param("structCode") String structCode, + @Param("invId") String invId, + @Param("invCode") String invCode); + + int unlockMoveInv(@Param("invId") String invId); + @Update(""" UPDATE wms_structattr SET lock_type = '3', @@ -49,6 +72,17 @@ public interface StrucAttrMapper extends BaseMapperX { int unlockOutbound(@Param("vehicleCode") String vehicleCode, @Param("invId") String invId); + @Update(""" + UPDATE wms_structattr + SET lock_type = '2', + task_code = #{taskCode} + WHERE struct_id = #{structId} + AND deleted = '0' + AND (lock_type IS NULL OR lock_type IN ('0', '1', '3')) + """) + int lockForTask(@Param("structId") String structId, + @Param("taskCode") String taskCode); + default PageResult selectPage(StrucAttrPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(StrucAttrDO::getStructCode, reqVO.getStructCode()) diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java index 8ef36bd0..672d1fa1 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java @@ -1,6 +1,8 @@ package cn.code.nl.module.wms.framework.rpc.config; import cn.code.nl.module.base.api.codegen.CodeGenApi; +import cn.code.nl.module.infra.api.config.ConfigApi; +import cn.code.nl.module.task.api.TransportTaskApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @@ -10,6 +12,6 @@ import org.springframework.context.annotation.Configuration; * @Date: 2026/7/22 13:50 */ @Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false) -@EnableFeignClients(clients = {CodeGenApi.class}) +@EnableFeignClients(clients = {CodeGenApi.class, TransportTaskApi.class, ConfigApi.class}) public class RpcConfiguration { } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java index 9ee0b052..084dcc7c 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java @@ -100,6 +100,8 @@ public interface IostorInvService { Long iostorinvId, Long iostorinvdtlId, String sectId); void manualAllocate(@Valid ManualAllocationSaveReqVO reqVO); + void oneClickSet(@Valid OutboundOneClickSetReqVO reqVO); + /** * 获得出入库单主表分页 * diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java index 3cab7820..bde3b7f7 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.StrUtil; import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.module.base.api.codegen.CodeGenApi; import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; +import cn.code.nl.module.task.api.TransportTaskApi; +import cn.code.nl.module.task.dto.TransportTaskCreateReqDTO; import cn.code.nl.module.wms.dal.dataobject.bsrealstorattr.BsrealStorAttrDO; import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO; import lombok.extern.slf4j.Slf4j; @@ -30,6 +32,7 @@ import cn.code.nl.module.wms.dal.mysql.iostorinvdis.IostorinvDisMapper; import cn.code.nl.module.wms.dal.mysql.structAttr.StrucAttrMapper; import cn.code.nl.module.wms.dal.dataobject.groupplate.GroupPlateDO; import cn.code.nl.module.wms.dal.dataobject.iostorinvdis.IostorinvDisDO; +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -38,6 +41,8 @@ import static cn.code.nl.framework.common.util.collection.CollectionUtils.conver import static cn.code.nl.framework.common.util.collection.CollectionUtils.convertSet; import static cn.code.nl.framework.common.util.collection.CollectionUtils.diffList; import static cn.code.nl.module.wms.enums.ErrorCodeConstants.*; +import static cn.code.nl.module.wms.enums.OutboundPointEnum.OUTBOUND_POINT_1; +import static cn.code.nl.module.wms.enums.OutboundPointEnum.OUTBOUND_POINT_2; /** * 出入库单主表 Service 实现类 @@ -70,6 +75,9 @@ public class IostorInvServiceImpl implements IostorInvService { @Resource private StrucAttrMapper strucAttrMapper; + @Resource + private TransportTaskApi transportTaskApi; + /** * 创建出库单及其明细。 */ @@ -300,10 +308,12 @@ public class IostorInvServiceImpl implements IostorInvService { @Override @Transactional(rollbackFor = Exception.class) public void allocateAll(OutboundAllocationReqVO reqVO) { + // 1. 校验出库单主表,后续所有明细分配必须归属于同一张有效单据。 IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId()); if (header == null) { throw exception(IOSTOR_INV_NOT_EXISTS); } + // 2. 按明细序号遍历整张出库单;库区未选择时不限制库区,选择后只匹配指定库区库存。 List details = iostorinvDtlMapper.selectList( new LambdaQueryWrapperX() .eq(IostorinvDtlDO::getIostorinvId, reqVO.getIostorinvId()) @@ -311,6 +321,7 @@ public class IostorInvServiceImpl implements IostorInvService { for (IostorinvDtlDO detail : details) { allocateDetail(header, detail, reqVO.getSectId()); } + // 3. 全部明细处理完成后,根据各明细已分配数统一回算主表状态。 refreshHeaderAllocationStatus(reqVO.getIostorinvId()); } @@ -331,11 +342,13 @@ public class IostorInvServiceImpl implements IostorInvService { @Override @Transactional(rollbackFor = Exception.class) public void cancelAllAllocations(OutboundAllocationReqVO reqVO) { + // 1. 只查询“生成”状态的分配记录;已下发、执行中或完成的记录禁止通过全部取消删除。 List generated = iostorinvDisMapper.selectList( new LambdaQueryWrapperX() .eq(IostorinvDisDO::getIostorinvId, reqVO.getIostorinvId()) .eqIfPresent(IostorinvDisDO::getSectId, reqVO.getSectId()) .eq(IostorinvDisDO::getWorkStatus, "10")); + // 2. 逐条释放冻结库存和出库锁,删除分配记录,最后回算明细及主表状态。 cancelGeneratedAllocations(reqVO.getIostorinvId(), generated); } @@ -399,6 +412,183 @@ public class IostorInvServiceImpl implements IostorInvService { refreshHeaderAllocationStatus(reqVO.getIostorinvId()); } + @Override + public void oneClickSet(OutboundOneClickSetReqVO reqVO) { + // 1. 校验出库单和当前选中明细,避免为其他单据的明细生成任务。 + IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId()); + if (header == null) { + throw exception(IOSTOR_INV_NOT_EXISTS); + } + IostorinvDtlDO detail = getOwnedDetail(reqVO.getIostorinvId(), reqVO.getIostorinvdtlId()); + // 2. 只处理尚未创建任务的“生成”状态分配记录,保证一键设置不会重复建任务。 + List pending = iostorinvDisMapper.selectList( + new LambdaQueryWrapperX() + .eq(IostorinvDisDO::getIostorinvId, String.valueOf(reqVO.getIostorinvId())) + .eq(IostorinvDisDO::getIostorinvdtlId, String.valueOf(reqVO.getIostorinvdtlId())) + .eq(IostorinvDisDO::getWorkStatus, "10") + .isNull(IostorinvDisDO::getTaskId) + .orderByAsc(IostorinvDisDO::getSeqNo)); + if (CollUtil.isEmpty(pending)) { + throw exception(IOSTOR_INV_NO_PENDING_ALLOCATION); + } + + // 3. 同一载具可能对应多个子卷分配,先按载具归组,后续一台载具只创建一个出库任务。 + Map> allocationsByVehicle = pending.stream() + .collect(java.util.stream.Collectors.groupingBy(IostorinvDisDO::getStoragevehicleCode, + LinkedHashMap::new, java.util.stream.Collectors.toList())); + // 4. 收集本次真正需要出库的仓位,用于识别同排货物中的目标箱和挡路箱。 + Set targetStructIds = pending.stream() + .map(IostorinvDisDO::getStructId) + .filter(Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + List targetStructs = strucAttrMapper.selectBatchIds(targetStructIds); + Map targetStructMap = convertMap(targetStructs, + StrucAttrDO::getStructId, item -> item); + + // 5. 按“库区 + 块 + 排 + 层”分组,同一物理通道内的货物一起规划左右出库顺序。 + Map> targetGroups = targetStructs.stream() + .collect(java.util.stream.Collectors.groupingBy(this::placementGroupKey, + LinkedHashMap::new, java.util.stream.Collectors.toList())); + // 6. 预先准备移库空位、两个出库点当前负载,以及本批任务共用的任务组和执行序号。 + List freeMoveLocations = selectFreeMoveLocations(header.getStorId(), targetStructIds); + Iterator freeLocationIterator = freeMoveLocations.iterator(); + long[] destinationLoads = loadDestinationCounts(); + long taskGroupId = IdUtil.getSnowflakeNextId(); + long sortSeq = 0; + + for (List targets : targetGroups.values()) { + StrucAttrDO sample = targets.get(0); + // 7. 查询当前排、当前层的全部仓位,按列号还原真实摆放顺序。 + List rowSlots = strucAttrMapper.selectList( + new LambdaQueryWrapperX() + .eq(StrucAttrDO::getSectId, sample.getSectId()) + .eq(StrucAttrDO::getBlockNum, sample.getBlockNum()) + .eq(StrucAttrDO::getRowNum, sample.getRowNum()) + .eq(StrucAttrDO::getLayerNum, sample.getLayerNum()) + .eq(StrucAttrDO::getIsUsed, true) + .orderByAsc(StrucAttrDO::getColNum)); + List slots = rowSlots.stream() + .map(item -> new OutboundPlacementPlanner.Slot(item.getStructId(), + Objects.requireNonNullElse(item.getColNum(), 0), + item.getStoragevehicleCode(), targetStructIds.contains(item.getStructId()))) + .toList(); + // 8. 根据 placement_type 计算左出、右出或双向最少移库方案,结果已按执行顺序排列。 + List actions = + OutboundPlacementPlanner.plan(sample.getPlacementType(), slots); + for (OutboundPlacementPlanner.Action action : actions) { + // 目标箱可直接从映射获取;挡路箱不在分配记录中,需要从当前排仓位补充来源信息。 + StrucAttrDO source = targetStructMap.get(action.structId()); + if (source == null) { + source = rowSlots.stream() + .filter(item -> item.getStructId().equals(action.structId())) + .findFirst().orElseThrow(); + } + if ("MOVE".equals(action.type())) { + // 9. 挡路箱创建移库任务,并锁定起点、目的仓位,防止规划后被其他任务占用。 + if (!freeLocationIterator.hasNext()) { + throw exception(IOSTOR_INV_NO_MOVE_LOCATION); + } + StrucAttrDO destination = freeLocationIterator.next(); + Long taskId = createAndIssueTask("010505", "移库任务", source.getStructCode(), + destination.getStructCode(), action.vehicleCode(), null, null, + taskGroupId, ++sortSeq, "MOVE", action.structId()); + strucAttrMapper.lockForTask(source.getStructId(), String.valueOf(taskId)); + strucAttrMapper.lockForTask(destination.getStructId(), String.valueOf(taskId)); + continue; + } + // 10. 非本次分配的载具只参与通道规划,不创建出库任务。 + List vehicleAllocations = allocationsByVehicle.get(action.vehicleCode()); + if (CollUtil.isEmpty(vehicleAllocations)) { + continue; + } + // 11. 比较两个出库点未完成任务数,选择当前负载较小的目的点。 + String destinationPoint = chooseDestination(destinationLoads); + Long taskId = createAndIssueTask("010503", "出库任务", source.getStructCode(), + destinationPoint, action.vehicleCode(), detail.getMaterialId(), + detail.getMaterialCode(), taskGroupId, ++sortSeq, "OUT", + vehicleAllocations.get(0).getIostorinvdisId()); + destinationLoads[destinationPoint.equals(OUTBOUND_POINT_1.getPointCode()) ? 0 : 1]++; + // 12. 同载具的全部分配记录关联同一个任务,并推进到“执行中/已下发”状态。 + vehicleAllocations.forEach(allocation -> { + IostorinvDisDO update = new IostorinvDisDO(); + update.setIostorinvdisId(allocation.getIostorinvdisId()); + update.setTaskId(String.valueOf(taskId)); + update.setPointCode(destinationPoint); + update.setWorkStatus("20"); + update.setIsIssued("1"); + iostorinvDisMapper.updateById(update); + }); + // 13. 锁定出库起点仓位,直到任务回调完成后再由后续流程释放。 + strucAttrMapper.lockForTask(source.getStructId(), String.valueOf(taskId)); + } + } + } + + private List selectFreeMoveLocations(String storId, Set excludedStructIds) { + return strucAttrMapper.selectList(new LambdaQueryWrapperX() + .eq(StrucAttrDO::getStorId, storId) + .eq(StrucAttrDO::getIsUsed, true) + .and(wrapper -> wrapper.isNull(StrucAttrDO::getStoragevehicleCode) + .or().eq(StrucAttrDO::getStoragevehicleCode, "")) + .and(wrapper -> wrapper.isNull(StrucAttrDO::getLockType) + .or().in(StrucAttrDO::getLockType, "0", "1")) + .notIn(CollUtil.isNotEmpty(excludedStructIds), StrucAttrDO::getStructId, excludedStructIds) + .orderByAsc(StrucAttrDO::getSectId) + .orderByAsc(StrucAttrDO::getBlockNum) + .orderByAsc(StrucAttrDO::getRowNum) + .orderByAsc(StrucAttrDO::getColNum)); + } + + private String placementGroupKey(StrucAttrDO item) { + return String.join("|", String.valueOf(item.getSectId()), String.valueOf(item.getBlockNum()), + String.valueOf(item.getRowNum()), String.valueOf(item.getLayerNum())); + } + + private long[] loadDestinationCounts() { + Long point1 = transportTaskApi.countUnfinishedByDestination( + OUTBOUND_POINT_1.getPointCode(), "WMS").getCheckedData(); + Long point2 = transportTaskApi.countUnfinishedByDestination( + OUTBOUND_POINT_2.getPointCode(), "WMS").getCheckedData(); + return new long[]{Objects.requireNonNullElse(point1, 0L), Objects.requireNonNullElse(point2, 0L)}; + } + + private String chooseDestination(long[] destinationLoads) { + return destinationLoads[0] <= destinationLoads[1] + ? OUTBOUND_POINT_1.getPointCode() : OUTBOUND_POINT_2.getPointCode(); + } + + private Long createAndIssueTask(String taskType, String taskName, String sourcePoint, + String destinationPoint, String vehicleCode, String materialId, + String materialCode, long taskGroupId, long sortSeq, + String bizType, String bizId) { + TransportTaskCreateReqDTO task = new TransportTaskCreateReqDTO(); + task.setTaskName(taskName); + task.setOwnerService("WMS"); + task.setBizType(bizType); + task.setBizId(bizId); + // TODO 填写出库任务在 WMS 中对应的任务回调处理器编码。 + task.setHandleCode("OUTTASK"); + task.setTaskType(taskType); + task.setAcsTaskType("7"); + // TODO 填写现场使用的 AGV 系统类型。 + task.setAgvSystemType("1"); + task.setPointCode1(sourcePoint); + task.setPointCode2(destinationPoint); + task.setVehicleCode(vehicleCode); + if (StrUtil.isNotBlank(materialId)) { + task.setMaterialId(Long.valueOf(materialId)); + } + task.setMaterialCode(materialCode); + task.setTaskGroupId(taskGroupId); + task.setSortSeq(sortSeq); + task.setIsAutoIssue("0"); + task.setProductArea("LK"); + task.setIsCreateFinish(true); + Long taskId = transportTaskApi.createTransportTask(task).getCheckedData(); + transportTaskApi.issueTransportTaskRollBack(taskId).getCheckedData(); + return taskId; + } + private void validateManualAllocationStatus(IostorinvDtlDO detail) { if (!Set.of("10", "20").contains(detail.getBillStatus())) { throw exception(IOSTOR_INV_MANUAL_STATUS_INVALID); @@ -446,6 +636,7 @@ public class IostorInvServiceImpl implements IostorInvService { } private BigDecimal allocateDetail(IostorInvDO header, IostorinvDtlDO detail, String sectId) { + // 从明细当前已分配数量继续补分,避免重复分配已经满足的计划数量。 BigDecimal assigned = Objects.requireNonNullElse(detail.getAssignQty(), BigDecimal.ZERO); BigDecimal remaining = detail.getPlanQty().subtract(assigned).max(BigDecimal.ZERO); BigDecimal allocatedNow = BigDecimal.ZERO; @@ -453,6 +644,7 @@ public class IostorInvServiceImpl implements IostorInvService { updateDetailAllocation(detail, assigned); return allocatedNow; } + // 按物料、批次、来源单号匹配入库可用库存;sectId 有值时额外限制库区。 List candidates = iostorinvDtlMapper.selectAllocationInventoryForUpdate( header.getStorId(), sectId, detail.getMaterialId(), detail.getPcsn(), @@ -461,7 +653,9 @@ public class IostorInvServiceImpl implements IostorInvService { if (remaining.signum() == 0) { break; } + // 单次分配量不超过明细剩余计划量,也不超过当前库存可用量。 BigDecimal allocationQty = remaining.min(candidate.getAvailableQty()); + // 写入生成状态分配记录,等待后续一键设置创建并下发搬运任务。 IostorinvDisDO allocation = new IostorinvDisDO(); allocation.setIostorinvdisId(IdUtil.getSnowflakeNextIdStr()); allocation.setIostorinvId(String.valueOf(header.getIostorinvId())); @@ -487,6 +681,7 @@ public class IostorInvServiceImpl implements IostorInvService { allocation.setHandType(false); iostorinvDisMapper.insert(allocation); + // 分配落库后立即加出库锁;并发情况下加锁失败则整个分配事务回滚。 int locked = strucAttrMapper.lockForOutbound( candidate.getStructId(), header.getBillType(), String.valueOf(header.getIostorinvId()), header.getBillCode()); @@ -494,6 +689,7 @@ public class IostorInvServiceImpl implements IostorInvService { throw exception(IOSTOR_INV_INVENTORY_INVALID); } + // 冻结本次分配的组盘数量,防止相同库存被其他出库单重复分配。 GroupPlateDO currentGroup = groupPlateMapper.selectById(candidate.getGroupId()); GroupPlateDO groupUpdate = new GroupPlateDO(); groupUpdate.setGroupId(candidate.getGroupId()); @@ -505,6 +701,7 @@ public class IostorInvServiceImpl implements IostorInvService { allocatedNow = allocatedNow.add(allocationQty); remaining = remaining.subtract(allocationQty); } + // 根据最终分配量更新明细的已分配数、未分配数及生成/分配中/分配完状态。 updateDetailAllocation(detail, assigned); return allocatedNow; } @@ -513,6 +710,7 @@ public class IostorInvServiceImpl implements IostorInvService { Set detailIds = new HashSet<>(); for (IostorinvDisDO allocation : generated) { detailIds.add(Long.valueOf(allocation.getIostorinvdtlId())); + // 1. 按载具、物料、批次定位原组盘库存,并退回该分配记录占用的冻结数量。 List groups = groupPlateMapper.selectList( new LambdaQueryWrapperX() .eq(GroupPlateDO::getVehicleCode, allocation.getStoragevehicleCode()) @@ -527,7 +725,9 @@ public class IostorInvServiceImpl implements IostorInvService { .subtract(allocation.getPlanQty()).max(BigDecimal.ZERO)); groupPlateMapper.updateById(update); }); + // 2. 仅删除调用方预先筛选出的“生成”状态分配记录。 iostorinvDisMapper.deleteById(allocation.getIostorinvdisId()); + // 3. 同一载具仍有其他分配时保留仓位锁;最后一条分配删除后才释放出库锁。 boolean vehicleStillAllocated = iostorinvDisMapper.selectCount( new LambdaQueryWrapperX() .eq(IostorinvDisDO::getStoragevehicleCode, @@ -537,6 +737,7 @@ public class IostorInvServiceImpl implements IostorInvService { allocation.getStoragevehicleCode(), String.valueOf(iostorinvId)); } } + // 4. 按取消后的剩余分配记录重新汇总每条受影响明细的分配数量和状态。 for (Long detailId : detailIds) { IostorinvDtlDO detail = iostorinvDtlMapper.selectById(detailId); BigDecimal assigned = iostorinvDisMapper.selectList( @@ -546,6 +747,7 @@ public class IostorInvServiceImpl implements IostorInvService { .reduce(BigDecimal.ZERO, BigDecimal::add); updateDetailAllocation(detail, assigned); } + // 5. 所有明细回算完成后,再统一更新主表的生成/分配中/分配完状态。 refreshHeaderAllocationStatus(iostorinvId); } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlanner.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlanner.java new file mode 100644 index 00000000..978208ea --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlanner.java @@ -0,0 +1,64 @@ +package cn.code.nl.module.wms.service.iostorinv; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 根据仓位放置类型规划一排货物的出库、移库顺序。 + */ +final class OutboundPlacementPlanner { + + private static final String DOUBLE_ACCESS = "01"; + private static final String LEFT_ACCESS = "02"; + private static final String RIGHT_ACCESS = "03"; + + private OutboundPlacementPlanner() { + } + + static List plan(String placementType, List slots) { + List occupied = slots.stream() + .filter(slot -> slot.vehicleCode() != null && !slot.vehicleCode().isBlank()) + .sorted(Comparator.comparingInt(Slot::column)) + .toList(); + if (occupied.stream().noneMatch(Slot::target)) { + return List.of(); + } + if (DOUBLE_ACCESS.equals(placementType)) { + List fromLeft = planFromSide(occupied, false); + List fromRight = planFromSide(occupied, true); + return moveCount(fromLeft) <= moveCount(fromRight) ? fromLeft : fromRight; + } + return planFromSide(occupied, RIGHT_ACCESS.equals(placementType)); + } + + private static List planFromSide(List occupied, boolean reverse) { + List ordered = new ArrayList<>(occupied); + if (reverse) { + ordered.sort(Comparator.comparingInt(Slot::column).reversed()); + } + int lastTargetIndex = -1; + for (int index = 0; index < ordered.size(); index++) { + if (ordered.get(index).target()) { + lastTargetIndex = index; + } + } + List result = new ArrayList<>(); + for (int index = 0; index <= lastTargetIndex; index++) { + Slot slot = ordered.get(index); + result.add(new Action(slot.target() ? "OUT" : "MOVE", + slot.structId(), slot.vehicleCode())); + } + return result; + } + + private static long moveCount(List actions) { + return actions.stream().filter(action -> "MOVE".equals(action.type())).count(); + } + + record Slot(String structId, int column, String vehicleCode, boolean target) { + } + + record Action(String type, String structId, String vehicleCode) { + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvService.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvService.java new file mode 100644 index 00000000..5e9f5131 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvService.java @@ -0,0 +1,34 @@ +package cn.code.nl.module.wms.service.moveinv; + +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvSaveReqVO; +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; + +import java.util.List; + +/** + * 移库管理 Service。 + */ +public interface MoveInvService { + + Long create(MoveInvSaveReqVO reqVO); + + void update(MoveInvSaveReqVO reqVO); + + void delete(Long id); + + MoveInvRespVO get(Long id); + + PageResult getPage(MoveInvPageReqVO reqVO); + + List getAvailableInventory(String storId, String sectId); + + List getAvailableTargetStructs(String sourceStructCode); + + MoveInvAutoCreateRespDTO autoCreate(MoveInvAutoCreateReqDTO reqDTO); +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvServiceImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvServiceImpl.java new file mode 100644 index 00000000..95535792 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveInvServiceImpl.java @@ -0,0 +1,370 @@ +package cn.code.nl.module.wms.service.moveinv; + +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO; +import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvDetailSaveReqVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvRespVO; +import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvSaveReqVO; +import cn.code.nl.module.wms.dal.dataobject.moveinv.MoveInvDO; +import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO; +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; +import cn.code.nl.module.wms.dal.mysql.moveinv.MoveInvMapper; +import cn.code.nl.module.wms.dal.mysql.moveinvdtl.MoveInvDtlMapper; +import cn.code.nl.module.wms.dal.mysql.structAttr.StrucAttrMapper; +import cn.code.nl.module.wms.dal.mysql.bsrealstorattr.BsrealStorAttrMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_CODE_GENERATE_FAILED; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_LOCK_FAILED; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_NOT_EXISTS; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_SOURCE_INVALID; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_STATUS_INVALID; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_TARGET_DUPLICATE; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_TARGET_INVALID; + +/** + * 移库管理 Service 实现。 + */ +@Service +public class MoveInvServiceImpl implements MoveInvService { + + private static final String GENERATED_STATUS = "10"; + private static final String MANUAL_CREATE_MODE = "01"; + private static final String API_CREATE_MODE = "02"; + + @Resource + private MoveInvMapper moveInvMapper; + @Resource + private MoveInvDtlMapper moveInvDtlMapper; + @Resource + private StrucAttrMapper strucAttrMapper; + @Resource + private CodeGenApi codeGenApi; + @Resource + private BsrealStorAttrMapper bsrealStorAttrMapper; + + /** + * 创建移库单。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long create(MoveInvSaveReqVO reqVO) { + return createMoveInv(reqVO, MANUAL_CREATE_MODE).getMoveinvId(); + } + + /** + * 更新移库单并替换全部明细。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void update(MoveInvSaveReqVO reqVO) { + MoveInvDO existed = moveInvMapper.selectById(reqVO.getMoveinvId()); + if (existed == null) { + throw exception(MOVE_INV_NOT_EXISTS); + } + if (!GENERATED_STATUS.equals(existed.getBillStatus())) { + throw exception(MOVE_INV_STATUS_INVALID); + } + strucAttrMapper.unlockMoveInv(String.valueOf(existed.getMoveinvId())); + List inventory = validateDetails(reqVO); + validateTargets(reqVO, inventory); + + MoveInvDO update = new MoveInvDO(); + update.setMoveinvId(existed.getMoveinvId()); + update.setBillType(reqVO.getBillType()); + update.setBizDate(reqVO.getBizDate()); + update.setStorId(reqVO.getStorId()); + update.setRemark(reqVO.getRemark()); + update.setDetailCount(reqVO.getDetails().size()); + update.setTotalQty(sumQty(reqVO.getDetails())); + moveInvMapper.updateById(update); + moveInvDtlMapper.deleteByMoveinvId(existed.getMoveinvId()); + insertDetailsAndLock(existed, reqVO.getDetails()); + } + + /** + * 删除生成状态移库单并释放仓位锁。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + MoveInvDO existed = moveInvMapper.selectById(id); + if (existed == null) { + throw exception(MOVE_INV_NOT_EXISTS); + } + if (!GENERATED_STATUS.equals(existed.getBillStatus())) { + throw exception(MOVE_INV_STATUS_INVALID); + } + strucAttrMapper.unlockMoveInv(String.valueOf(id)); + moveInvDtlMapper.deleteByMoveinvId(id); + moveInvMapper.deleteById(id); + } + + /** + * 查询移库单详情。 + */ + @Override + public MoveInvRespVO get(Long id) { + MoveInvDO moveInv = moveInvMapper.selectById(id); + if (moveInv == null) { + throw exception(MOVE_INV_NOT_EXISTS); + } + MoveInvRespVO respVO = convert(moveInv); + respVO.setDetails(moveInvDtlMapper.selectByMoveinvId(id)); + return respVO; + } + + /** + * 分页查询移库单。 + */ + @Override + public PageResult getPage(MoveInvPageReqVO reqVO) { + PageResult page = moveInvMapper.selectPage(reqVO); + List list = page.getList().stream().map(this::convert).toList(); + return new PageResult<>(list, page.getTotal()); + } + + /** + * 查询移库可用库存。 + */ + @Override + public List getAvailableInventory(String storId, String sectId) { + return strucAttrMapper.selectMoveAvailableInventory(storId, sectId, null); + } + + /** + * 查询来源仓位同库区同层的空闲目标仓位。 + */ + @Override + public List getAvailableTargetStructs(String sourceStructCode) { + StrucAttrDO source = strucAttrMapper.selectByStructCode(sourceStructCode); + if (source == null) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + return strucAttrMapper.selectMoveTargetCandidates( + source.getStorId(), source.getSectId(), source.getLayerNum()); + } + + /** + * 按来源载具自动寻找目标仓位并创建移库单。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public MoveInvAutoCreateRespDTO autoCreate(MoveInvAutoCreateReqDTO reqDTO) { + Set vehicleCodes = new HashSet<>(reqDTO.getVehicleCodes()); + if (vehicleCodes.size() != reqDTO.getVehicleCodes().size()) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + List inventory = strucAttrMapper.selectMoveAvailableInventory( + null, null, vehicleCodes); + if (inventory.size() != vehicleCodes.size()) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + String storId = inventory.get(0).getStorId(); + if (inventory.stream().anyMatch(item -> !storId.equals(item.getStorId()))) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + + Set selectedTargetIds = new HashSet<>(); + List details = new ArrayList<>(); + for (MoveInvInventoryRespVO item : inventory) { + StrucAttrDO source = strucAttrMapper.selectByStructCode(item.getStructCode()); + List candidates = strucAttrMapper.selectMoveTargetCandidates( + source.getStorId(), source.getSectId(), source.getLayerNum()); + StrucAttrDO target = MoveTargetStructSelector.select(source, candidates, selectedTargetIds); + selectedTargetIds.add(target.getStructId()); + details.add(buildDetail(item, target)); + } + + MoveInvSaveReqVO reqVO = new MoveInvSaveReqVO(); + reqVO.setBillType("3"); + reqVO.setBizDate(LocalDate.now().toString()); + reqVO.setStorId(storId); + reqVO.setDetails(details); + MoveInvDO moveInv = createMoveInv(reqVO, API_CREATE_MODE); + return new MoveInvAutoCreateRespDTO(moveInv.getMoveinvId(), moveInv.getBillCode()); + } + + /** + * 创建主表、明细并锁定来源和目标仓位。 + */ + private MoveInvDO createMoveInv(MoveInvSaveReqVO reqVO, String createMode) { + List inventory = validateDetails(reqVO); + validateTargets(reqVO, inventory); + CodeGenerateReqDTO codeReq = new CodeGenerateReqDTO(); + codeReq.setRuleCode("YK_CODE"); + CommonResult codeResult = codeGenApi.generate(codeReq); + if (codeResult == null || !codeResult.isSuccess() || codeResult.getData() == null) { + throw exception(MOVE_INV_CODE_GENERATE_FAILED); + } + + MoveInvDO moveInv = new MoveInvDO(); + moveInv.setBillCode(codeResult.getData()); + moveInv.setBillType(reqVO.getBillType()); + moveInv.setBizDate(reqVO.getBizDate()); + moveInv.setStorId(reqVO.getStorId()); + moveInv.setTotalQty(sumQty(reqVO.getDetails())); + moveInv.setDetailCount(reqVO.getDetails().size()); + moveInv.setBillStatus(GENERATED_STATUS); + moveInv.setRemark(reqVO.getRemark()); + moveInv.setCreateMode(createMode); + moveInvMapper.insert(moveInv); + insertDetailsAndLock(moveInv, reqVO.getDetails()); + return moveInv; + } + + /** + * 校验来源载具库存仍可用于移库。 + */ + private List validateDetails(MoveInvSaveReqVO reqVO) { + Set vehicleCodes = reqVO.getDetails().stream() + .map(MoveInvDetailSaveReqVO::getStoragevehicleCode) + .collect(Collectors.toSet()); + if (vehicleCodes.size() != reqVO.getDetails().size()) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + List inventory = strucAttrMapper.selectMoveAvailableInventory( + reqVO.getStorId(), null, vehicleCodes); + if (inventory.size() != vehicleCodes.size()) { + throw exception(MOVE_INV_SOURCE_INVALID); + } + return inventory; + } + + /** + * 校验手工选择的目标仓位与来源仓位同库区同层且不重复。 + */ + private void validateTargets(MoveInvSaveReqVO reqVO, List inventory) { + Map inventoryMap = inventory.stream() + .collect(Collectors.toMap(MoveInvInventoryRespVO::getStoragevehicleCode, item -> item)); + Set targetCodes = new HashSet<>(); + for (MoveInvDetailSaveReqVO detail : reqVO.getDetails()) { + if (!targetCodes.add(detail.getTurninStructCode())) { + throw exception(MOVE_INV_TARGET_DUPLICATE); + } + MoveInvInventoryRespVO source = inventoryMap.get(detail.getStoragevehicleCode()); + StrucAttrDO target = strucAttrMapper.selectByStructCode(detail.getTurninStructCode()); + if (target == null || !"1".equals(target.getLockType()) + || StringUtils.hasText(target.getStoragevehicleCode()) + || !source.getStorId().equals(target.getStorId()) + || !source.getSectId().equals(target.getSectId()) + || !source.getLayerNum().equals(target.getLayerNum())) { + throw exception(MOVE_INV_TARGET_INVALID); + } + detail.setTurnoutSectCode(source.getSectCode()); + detail.setTurnoutStructCode(source.getStructCode()); + detail.setTurninSectCode(target.getSectCode()); + detail.setMaterialCode(source.getMaterialCode()); + detail.setPcsn(source.getPcsn()); + detail.setQtyUnitId(source.getQtyUnitId()); + detail.setQtyUnitName(source.getQtyUnitName()); + detail.setQty(source.getQty()); + detail.setSourceBillCode(source.getSourceBillCode()); + } + } + + /** + * 插入全部明细,并分别给来源和目标仓位加锁。 + */ + private void insertDetailsAndLock(MoveInvDO moveInv, List details) { + int seq = 1; + for (MoveInvDetailSaveReqVO detail : details) { + MoveInvDtlDO row = new MoveInvDtlDO(); + row.setMoveinvId(moveInv.getMoveinvId()); + row.setSeqNo(seq++); + row.setTurnoutSectCode(detail.getTurnoutSectCode()); + row.setTurnoutStructCode(detail.getTurnoutStructCode()); + row.setMaterialCode(detail.getMaterialCode()); + row.setPcsn(detail.getPcsn()); + row.setQtyUnitId(detail.getQtyUnitId()); + row.setQtyUnitName(detail.getQtyUnitName()); + row.setQty(detail.getQty()); + row.setTurninSectCode(detail.getTurninSectCode()); + row.setTurninStructCode(detail.getTurninStructCode()); + row.setWorkStatus("01"); + row.setStoragevehicleCode(detail.getStoragevehicleCode()); + row.setIsIssued("0"); + row.setSourceBillCode(detail.getSourceBillCode()); + row.setRemark(detail.getRemark()); + moveInvDtlMapper.insert(row); + + String invId = String.valueOf(moveInv.getMoveinvId()); + if (strucAttrMapper.lockForMoveOut(detail.getTurnoutStructCode(), invId, moveInv.getBillCode()) != 1 + || strucAttrMapper.lockForMoveIn(detail.getTurninStructCode(), invId, moveInv.getBillCode()) != 1) { + throw exception(MOVE_INV_LOCK_FAILED); + } + } + } + + /** + * 汇总移库数量。 + */ + private BigDecimal sumQty(List details) { + return details.stream().map(MoveInvDetailSaveReqVO::getQty) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + /** + * 构造自动找位明细。 + */ + private MoveInvDetailSaveReqVO buildDetail(MoveInvInventoryRespVO source, StrucAttrDO target) { + MoveInvDetailSaveReqVO detail = new MoveInvDetailSaveReqVO(); + detail.setStoragevehicleCode(source.getStoragevehicleCode()); + detail.setTurnoutSectCode(source.getSectCode()); + detail.setTurnoutStructCode(source.getStructCode()); + detail.setTurninSectCode(target.getSectCode()); + detail.setTurninStructCode(target.getStructCode()); + detail.setMaterialCode(source.getMaterialCode()); + detail.setPcsn(source.getPcsn()); + detail.setQtyUnitId(source.getQtyUnitId()); + detail.setQtyUnitName(source.getQtyUnitName()); + detail.setQty(source.getQty()); + detail.setSourceBillCode(source.getSourceBillCode()); + return detail; + } + + /** + * 转换移库单响应。 + */ + private MoveInvRespVO convert(MoveInvDO source) { + MoveInvRespVO target = new MoveInvRespVO(); + target.setMoveinvId(source.getMoveinvId()); + target.setBillCode(source.getBillCode()); + target.setBillType(source.getBillType()); + target.setBizDate(source.getBizDate()); + target.setStorId(source.getStorId()); + if (source.getStorId() != null) { + var stor = bsrealStorAttrMapper.selectById(source.getStorId()); + if (stor != null) { + target.setStorName(stor.getStorName()); + } + } + target.setTotalQty(source.getTotalQty()); + target.setDetailCount(source.getDetailCount()); + target.setBillStatus(source.getBillStatus()); + target.setRemark(source.getRemark()); + target.setCreateMode(source.getCreateMode()); + target.setCreator(source.getCreator()); + target.setCreateTime(source.getCreateTime()); + return target; + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelector.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelector.java new file mode 100644 index 00000000..04f287c9 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelector.java @@ -0,0 +1,40 @@ +package cn.code.nl.module.wms.service.moveinv; + +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.MOVE_INV_NO_TARGET_STRUCT; + +/** + * 移库目标仓位选择器。 + */ +public final class MoveTargetStructSelector { + + private MoveTargetStructSelector() { + } + + /** + * 从候选仓位中选择距离来源仓位最近且未被本批次使用的仓位。 + * + * @param source 来源仓位 + * @param candidates 同库区同层候选仓位 + * @param excludedStructIds 本批次已使用仓位 + * @return 目标仓位 + */ + public static StrucAttrDO select(StrucAttrDO source, List candidates, + Set excludedStructIds) { + return candidates.stream() + .filter(item -> !excludedStructIds.contains(item.getStructId())) + .min(Comparator + .comparingInt((StrucAttrDO item) -> Math.abs(item.getColNum() - source.getColNum())) + .thenComparing(StrucAttrDO::getRowNum) + .thenComparing(StrucAttrDO::getColNum) + .thenComparing(StrucAttrDO::getBlockNum) + .thenComparing(StrucAttrDO::getStructId)) + .orElseThrow(() -> exception(MOVE_INV_NO_TARGET_STRUCT)); + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinvdtl/MoveInvDtlMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinvdtl/MoveInvDtlMapper.xml new file mode 100644 index 00000000..b2738995 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/moveinvdtl/MoveInvDtlMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + DELETE FROM wms_moveinvdtl WHERE moveinv_id = #{moveinvId} + + diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml index cc1b886b..2713b26f 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml @@ -1,12 +1,107 @@ - + - + - \ No newline at end of file + + + + + + UPDATE wms_structattr + SET lock_type = '4', + inv_type = '3', + inv_id = #{invId}, + inv_code = #{invCode} + WHERE deleted = '0' + AND struct_code = #{structCode} + AND lock_type = '1' + AND storagevehicle_code IS NOT NULL + AND storagevehicle_code != '' + + + + UPDATE wms_structattr + SET lock_type = '5', + inv_type = '3', + inv_id = #{invId}, + inv_code = #{invCode} + WHERE deleted = '0' + AND struct_code = #{structCode} + AND lock_type = '1' + AND (storagevehicle_code IS NULL OR storagevehicle_code = '') + + + + UPDATE wms_structattr + SET lock_type = '1', + inv_type = NULL, + inv_id = NULL, + inv_code = NULL + WHERE deleted = '0' + AND inv_id = #{invId} + AND lock_type IN ('4', '5') + + diff --git a/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlannerTest.java b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlannerTest.java new file mode 100644 index 00000000..326751b1 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/OutboundPlacementPlannerTest.java @@ -0,0 +1,47 @@ +package cn.code.nl.module.wms.service.iostorinv; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class OutboundPlacementPlannerTest { + + @Test + void shouldMoveBlockingBoxBeforeOutboundFromLeft() { + List actions = OutboundPlacementPlanner.plan("02", List.of( + new OutboundPlacementPlanner.Slot("S1", 1, "BLOCKER", false), + new OutboundPlacementPlanner.Slot("S2", 2, "TARGET", true), + new OutboundPlacementPlanner.Slot("S3", 3, null, false))); + + assertEquals(List.of( + new OutboundPlacementPlanner.Action("MOVE", "S1", "BLOCKER"), + new OutboundPlacementPlanner.Action("OUT", "S2", "TARGET")), actions); + } + + @Test + void shouldMoveBlockingBoxBeforeOutboundFromRight() { + List actions = OutboundPlacementPlanner.plan("03", List.of( + new OutboundPlacementPlanner.Slot("S1", 1, null, false), + new OutboundPlacementPlanner.Slot("S2", 2, "TARGET", true), + new OutboundPlacementPlanner.Slot("S3", 3, "BLOCKER", false))); + + assertEquals(List.of( + new OutboundPlacementPlanner.Action("MOVE", "S3", "BLOCKER"), + new OutboundPlacementPlanner.Action("OUT", "S2", "TARGET")), actions); + } + + @Test + void shouldChooseSideWithFewerBlockingBoxesForDoubleAccess() { + List actions = OutboundPlacementPlanner.plan("01", List.of( + new OutboundPlacementPlanner.Slot("S1", 1, "BLOCKER-1", false), + new OutboundPlacementPlanner.Slot("S2", 2, "BLOCKER-2", false), + new OutboundPlacementPlanner.Slot("S3", 3, "TARGET", true), + new OutboundPlacementPlanner.Slot("S4", 4, "BLOCKER-3", false))); + + assertEquals(List.of( + new OutboundPlacementPlanner.Action("MOVE", "S4", "BLOCKER-3"), + new OutboundPlacementPlanner.Action("OUT", "S3", "TARGET")), actions); + } +} diff --git a/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelectorTest.java b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelectorTest.java new file mode 100644 index 00000000..fd9be125 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/moveinv/MoveTargetStructSelectorTest.java @@ -0,0 +1,34 @@ +package cn.code.nl.module.wms.service.moveinv; + +import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +class MoveTargetStructSelectorTest { + + @Test + void shouldSelectNearestAvailableStructAndExcludeUsedTarget() { + StrucAttrDO source = struct("S", 1, 10, 2, 1); + StrucAttrDO farther = struct("T1", 1, 15, 2, 1); + StrucAttrDO nearest = struct("T2", 1, 11, 2, 1); + StrucAttrDO used = struct("T3", 1, 9, 2, 1); + + StrucAttrDO result = MoveTargetStructSelector.select( + source, List.of(farther, nearest, used), Set.of("T3")); + + Assertions.assertEquals("T2", result.getStructId()); + } + + private StrucAttrDO struct(String id, int row, int col, int layer, int block) { + return StrucAttrDO.builder() + .structId(id) + .rowNum(row) + .colNum(col) + .layerNum(layer) + .blockNum(block) + .build(); + } +} diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts index 90d1c2b5..9bc437fb 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts @@ -240,6 +240,13 @@ export function manualAllocate(data: { return requestClient.post('/wms/iostor-inv/manual-allocate', data); } +export function oneClickSet(iostorinvId: string, iostorinvdtlId: string) { + return requestClient.post('/wms/iostor-inv/one-click-set', { + iostorinvId, + iostorinvdtlId, + }); +} + /** 新增出入库单主表 */ export function createIostorInv(data: WmsIostorInvApi.IostorInv) { return requestClient.post('/wms/iostor-inv/create', data); diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/moveinv/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/moveinv/index.ts new file mode 100644 index 00000000..09f43a79 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/moveinv/index.ts @@ -0,0 +1,103 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace WmsMoveInvApi { + export interface Detail { + moveinvdtlId?: string; + storagevehicleCode: string; + turnoutSectCode?: string; + turnoutStructCode: string; + turninSectCode?: string; + turninStructCode: string; + materialCode?: string; + pcsn?: string; + qtyUnitId?: string; + qtyUnitName?: string; + qty: number; + sourceBillCode?: string; + remark?: string; + } + + export interface MoveInv { + moveinvId?: string; + billCode?: string; + billType: string; + bizDate: string; + storId: string; + storName?: string; + totalQty?: number; + detailCount?: number; + billStatus?: string; + remark?: string; + creatorName?: string; + createTime?: string; + details: Detail[]; + } + + export interface Inventory { + structId: string; + structCode: string; + structName: string; + sectId: string; + sectCode: string; + sectName: string; + storId: string; + storagevehicleCode: string; + layerNum: number; + materialCode?: string; + pcsn?: string; + qtyUnitId?: string; + qtyUnitName?: string; + qty: number; + sourceBillCode?: string; + } + + export interface Struct { + structId: string; + structCode: string; + structName: string; + sectCode: string; + sectName: string; + layerNum: number; + } +} + +export function getMoveInvPage(params: PageParam) { + return requestClient.get>( + '/wms/move-inv/page', + { params }, + ); +} + +export function getMoveInv(id: string) { + return requestClient.get('/wms/move-inv/get', { + params: { id }, + }); +} + +export function createMoveInv(data: WmsMoveInvApi.MoveInv) { + return requestClient.post('/wms/move-inv/create', data); +} + +export function updateMoveInv(data: WmsMoveInvApi.MoveInv) { + return requestClient.put('/wms/move-inv/update', data); +} + +export function deleteMoveInv(id: string) { + return requestClient.delete('/wms/move-inv/delete', { params: { id } }); +} + +export function getMoveAvailableInventory(storId: string, sectId?: string) { + return requestClient.get( + '/wms/move-inv/availableInventory', + { params: { storId, sectId } }, + ); +} + +export function getMoveTargetStructs(sourceStructCode: string) { + return requestClient.get( + '/wms/move-inv/availableTargetStructs', + { params: { sourceStructCode } }, + ); +} diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/allocation.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/allocation.vue index 908aad5c..5268eb29 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/allocation.vue +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/allocation.vue @@ -14,6 +14,7 @@ import { cancelAllAllocations, getIostorInvDetails, getOutboundAllocations, + oneClickSet, } from '#/api/wms/iostorinv'; import { getSectAttrSimpleList } from '#/api/wms/sectattr'; @@ -204,8 +205,18 @@ async function handleManualSuccess() { emit('success'); } -function todo(name: string) { - message.info(`${name}功能待开发`); +async function handleOneClickSet() { + if (!header.value || !requireSelectedDetail()) return; + loading.value = true; + try { + await oneClickSet(header.value.iostorinvId, selectedDetailId.value!); + await loadDetails(); + await loadSelectedAllocations(); + emit('success'); + message.success('一键设置并下发任务完成'); + } finally { + loading.value = false; + } } function statusLabel(value?: string) { @@ -258,7 +269,6 @@ const [Modal, modalApi] = useVbenModal({ - @@ -280,7 +290,12 @@ const [Modal, modalApi] = useVbenModal({ -