skyouc
2025-07-04 49b8b52ae6dfb28e3e9a741bb277c231bd13418d
库存明细出库
1个文件已删除
10个文件已修改
1个文件已添加
801 ■■■■■ 已修改文件
zy-asrs-admin/src/views/loc/loc/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-admin/src/views/loc/locDetl/index.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-admin/src/views/out/flat/index.vue 361 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-common/src/main/java/com/zy/asrs/common/domain/param/StockOutParam.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/controller/OutController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/controller/PickSheetController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/PickSheet.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/dto/OrderOutMergeDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/param/OrderOutMergeParam.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/param/StockOutParam.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/manage/OutManage.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/service/impl/PickSheetServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-admin/src/views/loc/loc/index.vue
@@ -138,7 +138,6 @@
    ellipsis: true,
    ...getColumnSearchProps('memo'),
  },
  {
    title: formatMessage('common.operation', '操作'),
    name: 'oper',
zy-asrs-admin/src/views/loc/locDetl/index.vue
@@ -97,13 +97,6 @@
    ellipsis: true,
    ...getColumnSearchProps('model'),
  },
  // {
  //   title: formatMessage('db.man_loc_detl.model', '品类'),
  //   dataIndex: 'tagId$',
  //   width: 140,
  //   ellipsis: true,
  //   ...getColumnSearchProps('tagId$'),
  // },
  {
    title: formatMessage('db.man_loc_detl.dewell', '入库时间'),
    dataIndex: 'dewell$',
@@ -132,12 +125,19 @@
    ellipsis: true,
    ...getColumnSearchProps('anfme'),
  },
  {
    title: formatMessage('db.man_loc_detl.freeze', '是否冻结'),
    dataIndex: 'freeze$',
  // {
  //   title: formatMessage('db.man_loc_detl.freeze', '是否冻结'),
  //   dataIndex: 'freeze$',
  //   width: 140,
  //   ellipsis: true,
  //   ...getColumnSearchProps('freeze$'),
  // },
    {
    title: formatMessage('db.man_loc_detl.type', '类型'),
    dataIndex: 'type',
    width: 140,
    ellipsis: true,
    ...getColumnSearchProps('freeze$'),
    ...getColumnSearchProps('type'),
  },
  {
    title: formatMessage('db.man_loc_detl.status', '状态'),
@@ -146,20 +146,6 @@
    ellipsis: true,
    ...getColumnSearchProps('status$'),
  },
  // {
  //   title: formatMessage('db.man_loc_detl.create_time', '添加时间'),
  //   dataIndex: 'createTime$',
  //   width: 140,
  //   ellipsis: true,
  //   ...getColumnSearchProps('createTime$'),
  // },
  // {
  //   title: formatMessage('db.man_loc_detl.create_by', '添加人员'),
  //   dataIndex: 'createBy$',
  //   width: 140,
  //   ellipsis: true,
  //   ...getColumnSearchProps('createBy$'),
  // },
  {
    title: formatMessage('db.man_loc_detl.update_time', '修改时间'),
    dataIndex: 'updateTime$',
@@ -180,6 +166,14 @@
    width: 140,
    ellipsis: true,
    ...getColumnSearchProps('memo'),
  },
  {
    title: formatMessage('common.operation', '操作'),
    name: 'oper',
    dataIndex: 'oper',
    key: 'oper',
    width: 140,
    fixed: 'right',
  },
];
@@ -236,9 +230,43 @@
}
const handleEdit = (item) => {
  editChild.value.open = true;
  editChild.value.formData = item == null ? editChild.value.initFormData : JSON.parse(JSON.stringify(item));
  editChild.value.isSave = item == null;
  let content = "是否确认生成-->出库任务!!"
  let type = 2
  if (item?.locNo.indexOf("B") >= 0 || item?.locNo.indexOf("C") >= 0) {
      content = "是否确认生成-->拣货单!!"
      type = 1
  }
  Modal.confirm({
    title: formatMessage('page.delete', '出库'),
    content: formatMessage('page.delete.confirm', content),
    maskClosable: true,
    onOk: async () => {
      const hide = message.loading(formatMessage('common.loading', '请求中'));
      try {
        let params = {
          outType: type,
          locDetls: [item]
        }
        post('/api/out/locs/stock', params).then(resp => {
          let result = resp.data;
          if (result.code === 200) {
            console.log(result);
            message.success(result.msg);
          } else {
            message.error(result.msg);
          }
          getPage()
          hide()
        })
      } catch (error) {
        message.error(formatMessage('common.fail', '请求失败'));
      }
    },
  });
  // editChild.value.open = true;
  // editChild.value.formData = item == null ? editChild.value.initFormData : JSON.parse(JSON.stringify(item));
  // editChild.value.isSave = item == null;
}
const handleDel = (rows) => {
@@ -316,12 +344,13 @@
          style="width: 140px;margin-right: 10px;" />
        <a-input v-model:value="searchParam.batch" :placeholder="formatMessage('page.locDetl.batch.input', '请输入批号')"
          style="width: 140px;margin-right: 10px;" />
        <a-select v-model:value="searchParam.tagId" :placeholder="formatMessage('page.locDetl.orderNo.input', '请选择品类')" :options="[
          { label: '默认分类', value: 10 },{ label: '机油', value: 11 }, { label: '变速箱油', value: 17 }, { label: '火花塞', value: 18 },
          { label: '养护品', value: 25 },{ label: '油漆耗材', value: 26 }, { label: '球头摆臂', value: 27 }, { label: '砂纸类', value: 31 },
          { label: '菜瓜布', value: 32 }, { label: '遮蔽类', value: 33 }, { label: '抛光类', value: 34 }, { label: '除尘类', value: 35 },
          { label: '漏斗类', value: 36 }, { label: '防护类', value: 37 }, { label: '烤房保养类', value: 38 }, { label: '调漆罐', value: 39 },
          { label: '喷枪', value: 40 }, { label: '费斯托系列', value: 41 },]" style="width: 140px;margin-right: 10px;">
        <a-select v-model:value="searchParam.tagId" :placeholder="formatMessage('page.locDetl.orderNo.input', '请选择品类')"
          :options="[
            { label: '默认分类', value: 10 }, { label: '机油', value: 11 }, { label: '变速箱油', value: 17 }, { label: '火花塞', value: 18 },
            { label: '养护品', value: 25 }, { label: '油漆耗材', value: 26 }, { label: '球头摆臂', value: 27 }, { label: '砂纸类', value: 31 },
            { label: '菜瓜布', value: 32 }, { label: '遮蔽类', value: 33 }, { label: '抛光类', value: 34 }, { label: '除尘类', value: 35 },
            { label: '漏斗类', value: 36 }, { label: '防护类', value: 37 }, { label: '烤房保养类', value: 38 }, { label: '调漆罐', value: 39 },
            { label: '喷枪', value: 40 }, { label: '费斯托系列', value: 41 },]" style="width: 140px;margin-right: 10px;">
        </a-select>
        <a-input-search v-model:value="searchInput" :placeholder="formatMessage('page.input', '请输入')"
          style="width: 200px;" @search="onSearch" />
@@ -339,9 +368,8 @@
      <template #bodyCell="{ column, text, record }">
        <template v-if="column.dataIndex === 'oper'">
          <div style="display: flex;justify-content: space-evenly;">
            <a-button type="link" primary @click="handleEdit(record)">{{ formatMessage('page.edit', '编辑') }}</a-button>
            <a-button type="link" danger @click="handleDel([record])">{{ formatMessage('page.delete', '删除')
            }}</a-button>
            <a-button type="link" primary @click="handleEdit(record)">{{ formatMessage('page.edit', '手动出库') }}</a-button>
            <!-- <a-button type="link" danger @click="handleDel([record])">{{ formatMessage('page.delete', '删除')}}</a-button> -->
          </div>
        </template>
      </template>
zy-asrs-admin/src/views/out/flat/index.vue
@@ -2,24 +2,18 @@
    <div class="table-header">
        <div style="display: flex;padding: 10px;">
            <div style="margin-right: 10px;">
                <a-input v-model:value="searchParam.pickNo"
                         placeholder="请输拣货单编号"
                         style="width: 200px;margin-right: 10px;"/>
                <a-input v-model:value="searchParam.waveNo" placeholder="请输入波次号"
                                style="width: 200px;" />
                <a-input v-model:value="searchParam.pickNo" placeholder="请输拣货单编号"
                    style="width: 200px;margin-right: 10px;" />
                <a-input v-model:value="searchParam.waveNo" placeholder="请输入波次号" style="width: 200px;" />
                <a-button @click="queryPickSheets" type="primary" style="margin-left: 35px">查询</a-button>
            </div>
        </div>
    </div>
    <a-table :columns="columns"
             :data-source="datasource" bordered
             :defaultExpandAllRows="false"
             :row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
             :scroll="{y: columns.length * 140}"
             style="margin: 5px"
    >
        <template #bodyCell="{column, record, index}">
    <a-table :columns="columns" :data-source="datasource" bordered :defaultExpandAllRows="false"
        :row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
        :scroll="{ y: columns.length * 140 }" style="margin: 5px">
        <template #bodyCell="{ column, record, index }">
            <template v-if="column.key === 'number'">
                {{ index + 1 }}
            </template>
@@ -38,7 +32,7 @@
        </template>
    </a-table>
    <a-modal ref="sheetDetl" v-model:open="show" :width="'80%'" title="拣货单明细" @ok="handleOk" @cancel="cancel"
             :okText="isPrint ? '打印' : '确认'" cancel-text="关闭">
        :okText="isPrint ? '打印' : '确认'" cancel-text="关闭">
        <div id="pcik-detl">
            <div class="component-header">
                <div>
@@ -50,15 +44,11 @@
                    </h3>
                </div>
                <div class="qrcode">
                    <a-qrcode :value="selectDetl.pickNo" :size="100" :bordered="false"/>
                    <a-qrcode :value="selectDetl.pickNo" :size="100" :bordered="false" />
                </div>
            </div>
            <a-table :columns="childNodes"
                     :data-source="childList"
                     bordered
                     :pagination="{hideOnSinglePage: true}"
            >
                <template #bodyCell="{column, record, index}">
            <a-table :columns="childNodes" :data-source="childList" bordered :pagination="{ hideOnSinglePage: true }">
                <template #bodyCell="{ column, record, index }">
                    <template v-if="column.key === 'number'">
                        {{ index + 1 }}
                    </template>
@@ -74,183 +64,184 @@
</template>
<script>
    import {post, get} from "@/utils/request.js";
    import {message, Modal} from "ant-design-vue";
    import {createVNode} from 'vue';
    import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
    import printJS from 'print-js'
import { post, get } from "@/utils/request.js";
import { message, Modal } from "ant-design-vue";
import { createVNode } from 'vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import printJS from 'print-js'
    export default {
        name: "out-stock-flat",
        data() {
            return {
                searchParam: {
                    pickNo: '',
                    waveNo:''
                },
                state: {
                    selectedRowKeys: []
                },
                searchInput: '',
                columns: [
                    {key: 'number', title: '序号', dataIndex: 'number', align: 'center', width: '65px'},
                    {key: 'pickNo', title: '单号', dataIndex: 'pickNo', align: 'center', width: '205px'},
                    {key: 'waveNo', title: '波次号', dataIndex: 'waveNo', align: 'center', width: '155px'},
                    {key: 'anfme', title: '数量', dataIndex: 'anfme', align: 'center', width: '85px'},
                    {key: 'status', title: '单据状态', dataIndex: 'status', align: 'center', width: '105px'},
                    {key: 'createdTime', title: '创建时间', dataIndex: 'createdTime', align: 'center', width: '185px'},
                    {key: 'updatedTime', title: '修改时间', dataIndex: 'updatedTime', align: 'center', width: '185px'},
                    {key: 'memo', title: '备注', dataIndex: '', align: 'center'},
                    {
                        key: 'operate',
                        title: '操作',
                        dataIndex: 'operate',
                        fixed: 'right',
                        align: 'center',
                        width: '155px'
                    }
                ],
                childNodes: [
                    {key: 'number', title: '序号', dataIndex: 'number'},
                    {key: 'maktx', title: '物料名称', dataIndex: 'maktx'},
                    {key: 'matnr', title: '物料编码', dataIndex: 'matnr'},
                    {key: 'batch', title: '批号', dataIndex: 'batch'},
                    {key: 'locNo', title: '库位', dataIndex: 'locNo'},
                    {key: 'barcode', title: '拖盘码', dataIndex: 'barcode'},
                    {key: 'anfme', title: '数量', dataIndex: 'anfme'},
                    {key: 'memo', title: '备注', dataIndex: ''},
                    {key: 'status', title: '单据状态', dataIndex: 'status'},
                    // {key: 'operate', title: '操作', dataIndex: 'operate'}
                ],
                datasource: [],
                childList: [],
                show: false,
                isPrint: false,
                selectDetl: {},
export default {
    name: "out-stock-flat",
    data() {
        return {
            searchParam: {
                pickNo: '',
                waveNo: ''
            },
            state: {
                selectedRowKeys: []
            },
            searchInput: '',
            columns: [
                { key: 'number', title: '序号', dataIndex: 'number', align: 'center', width: '65px' },
                { key: 'pickNo', title: '单号', dataIndex: 'pickNo', align: 'center', width: '205px' },
                { key: 'waveNo', title: '波次号', dataIndex: 'waveNo', align: 'center', width: '205px' },
                { key: 'type$', title: '类型', dataIndex: 'type$', align: 'center', width: '155px' },
                { key: 'anfme', title: '数量', dataIndex: 'anfme', align: 'center', width: '85px' },
                { key: 'status', title: '单据状态', dataIndex: 'status', align: 'center', width: '105px' },
                { key: 'createdTime', title: '创建时间', dataIndex: 'createdTime', align: 'center', width: '185px' },
                { key: 'updatedTime', title: '修改时间', dataIndex: 'updatedTime', align: 'center', width: '185px' },
                { key: 'memo', title: '备注', dataIndex: '', align: 'center' },
                {
                    key: 'operate',
                    title: '操作',
                    dataIndex: 'operate',
                    fixed: 'right',
                    align: 'center',
                    width: '155px'
                }
            ],
            childNodes: [
                { key: 'number', title: '序号', dataIndex: 'number' },
                { key: 'maktx', title: '物料名称', dataIndex: 'maktx' },
                { key: 'matnr', title: '物料编码', dataIndex: 'matnr' },
                { key: 'batch', title: '批号', dataIndex: 'batch' },
                { key: 'locNo', title: '库位', dataIndex: 'locNo' },
                { key: 'barcode', title: '拖盘码', dataIndex: 'barcode' },
                { key: 'anfme', title: '数量', dataIndex: 'anfme' },
                { key: 'memo', title: '备注', dataIndex: '' },
                { key: 'status', title: '单据状态', dataIndex: 'status' },
                // {key: 'operate', title: '操作', dataIndex: 'operate'}
            ],
            datasource: [],
            childList: [],
            show: false,
            isPrint: false,
            selectDetl: {},
        }
    },
    mounted() {
        //获取拣货单数据源
        this.getOutFlatSheet();
    },
    methods: {
        queryPickSheets() {
            this.getOutFlatSheet()
        },
        handleOk() {
            if (this.isPrint) {
                printJS('pcik-detl', 'html')
            }
        },
        mounted() {
            //获取拣货单数据源
            this.getOutFlatSheet();
        cancel() {
            this.isPrint = false
        },
        onSelectChange(selectedRowKeys) {
            state.selectedRowKeys = selectedRowKeys;
        },
        /**
         * 打印
         */
        handlePrint(column, record) {
            this.show = true
            this.isPrint = true
            this.selectDetl = record
            this.getSheetDetl(record)
        },
        /**
         * 搜索
         */
        onSearch() {
            console.log(this)
        },
        methods: {
            queryPickSheets() {
              this.getOutFlatSheet()
            },
        showDeleteConfirm(record) {
            let that = this
            Modal.confirm({
                title: '是否确认删除当前拣货单',
                icon: createVNode(ExclamationCircleOutlined),
                content: '连同明细一起删除',
                okText: '确认',
                okType: 'danger',
                cancelText: '取消',
                onOk() {
                    that.removeRow(record)
                },
                onCancel() {
                    console.log('Cancel');
                },
            });
        },
            handleOk() {
                if (this.isPrint) {
                    printJS('pcik-detl', 'html')
        //删除当前行
        removeRow(record) {
            let that = this
            get('/api/pick/flat/remove/' + record.id).then((resp) => {
                let result = resp.data;
                if (result.code == 200) {
                    that.getOutFlatSheet()
                    message.success(formatMessage('page.delete.success', '删除成功'));
                } else {
                    message.error(result.msg);
                }
            },
            cancel() {
                this.isPrint = false
            },
            onSelectChange(selectedRowKeys) {
                state.selectedRowKeys = selectedRowKeys;
            },
            /**
             * 打印
             */
            handlePrint(column, record) {
                this.show = true
                this.isPrint = true
                this.selectDetl = record
                this.getSheetDetl(record)
            },
            /**
             * 搜索
             */
            onSearch() {
                console.log(this)
            },
            showDeleteConfirm(record) {
                let that = this
                Modal.confirm({
                    title: '是否确认删除当前拣货单',
                    icon: createVNode(ExclamationCircleOutlined),
                    content: '连同明细一起删除',
                    okText: '确认',
                    okType: 'danger',
                    cancelText: '取消',
                    onOk() {
                        that.removeRow(record)
                    },
                    onCancel() {
                        console.log('Cancel');
                    },
                });
            },
            //删除当前行
            removeRow(record) {
                let that = this
                get('/api/pick/flat/remove/' + record.id).then((resp) => {
                    let result = resp.data;
                    if (result.code == 200) {
                        that.getOutFlatSheet()
                        message.success(formatMessage('page.delete.success', '删除成功'));
                    } else {
                        message.error(result.msg);
                    }
                })
            },
            //查看明细
            viewDetail(column, record) {
                this.show = !this.show
                this.isPrint = false
                this.selectDetl = record
                this.getSheetDetl(record)
            },
            getOutFlatSheet() {
                let that = this
                post('/api/pick/flat/page', {page: {currnt: 1, size: 10},  params: {pickNo: this.searchParam.pickNo, waveNo: this.searchParam.waveNo}}).then((resp) => {
                    let result = resp.data;
                    if (result.code == 200) {
                        // message.success(formatMessage('page.add.success', '成功'));
                        that.datasource = result.data
                    } else {
                        message.error(result.msg);
                    }
                })
            },
            getSheetDetl(record) {
                let that = this
                post('/api/pick/flat/detl/page', {
                    page: {currnt: 1, size: 10},
                    params: {pickId: record.id}
                }).then((resp) => {
                    let result = resp.data;
                    if (result.code == 200) {
                        // message.success(formatMessage('page.add.success', '成功'));
                        that.childList = result.data
                    } else {
                        message.error(result.msg);
                    }
                })
            },
        }
            })
        },
        //查看明细
        viewDetail(column, record) {
            this.show = !this.show
            this.isPrint = false
            this.selectDetl = record
            this.getSheetDetl(record)
        },
        getOutFlatSheet() {
            let that = this
            post('/api/pick/flat/page', { page: { currnt: 1, size: 10 }, params: { pickNo: this.searchParam.pickNo, waveNo: this.searchParam.waveNo } }).then((resp) => {
                let result = resp.data;
                if (result.code == 200) {
                    // message.success(formatMessage('page.add.success', '成功'));
                    that.datasource = result.data
                } else {
                    message.error(result.msg);
                }
            })
        },
        getSheetDetl(record) {
            let that = this
            post('/api/pick/flat/detl/page', {
                page: { currnt: 1, size: 10 },
                params: { pickId: record.id }
            }).then((resp) => {
                let result = resp.data;
                if (result.code == 200) {
                    // message.success(formatMessage('page.add.success', '成功'));
                    that.childList = result.data
                } else {
                    message.error(result.msg);
                }
            })
        },
    }
}
</script>
<style scoped>
    .component-header {
        display: flex;
    }
.component-header {
    display: flex;
}
    .component-header > div {
        flex: 1;
    }
.component-header>div {
    flex: 1;
}
    .qrcode {
        display: flex;
        justify-content: flex-end;
        margin-right: 30px;
    }
.qrcode {
    display: flex;
    justify-content: flex-end;
    margin-right: 30px;
}
</style>
zy-asrs-common/src/main/java/com/zy/asrs/common/domain/param/StockOutParam.java
File was deleted
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/controller/OutController.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.wms.asrs.entity.param.StockOutParam;
import com.zy.asrs.framework.common.R;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wms.asrs.entity.Order;
@@ -24,6 +25,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@RestController
@@ -130,6 +132,16 @@
        return R.ok();
    }
    @PostMapping("/out/locs/stock")
    @OperationLog("手动出库")
    public R stockOut(@RequestBody StockOutParam param) {
        if (Objects.isNull(param)) {
            throw new CoolException("参数不能为空!!");
        }
        outManage.outLocStock(param);
        return R.ok();
    }
    @PostMapping("/out/wave/generate")
    @OperationLog("生成波次")
    @Transactional
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/controller/PickSheetController.java
@@ -24,7 +24,7 @@
    @PostMapping("/pick/flat/page")
    public R getOutFlatSheet(@RequestBody PageRequest params) {
        IPage page = pickSheetService.getOutFlatSheet(params);
        return R.ok(page.getRecords());
        return R.ok().add(page.getRecords());
    }
    /**
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/PickSheet.java
@@ -3,10 +3,14 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zy.asrs.framework.common.SpringUtils;
import com.zy.asrs.wms.asrs.service.PickSheetService;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
@@ -28,6 +32,9 @@
     * 单据编号
     */
    private String pickNo;
    @ApiModelProperty("拣货单类型 {1. 波次单, 2. 库存出库}")
    private Integer type;
    /**
     * 波次标识
@@ -86,4 +93,16 @@
    private Integer deleted;
    private static final long serialVersionUID = 1L;
    public String getType$() {
        if (Objects.isNull(type)) {
            return "波次单据";
        }
        if (type == 2) {
            return "手动出库";
        } else {
            return "波次单";
        }
    }
}
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/dto/OrderOutMergeDto.java
@@ -3,10 +3,12 @@
import com.zy.asrs.common.utils.Synchro;
import com.zy.asrs.wms.asrs.entity.param.FieldParam;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class OrderOutMergeDto {
    private Long locId;
@@ -23,6 +25,8 @@
    private Double anfme;
    private String portSite;
    private Long operationPort;
    private List<FieldParam> fieldParams;
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/param/OrderOutMergeParam.java
@@ -1,30 +1,45 @@
package com.zy.asrs.wms.asrs.entity.param;
import com.mysql.cj.log.Log;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@ApiModel(value = "OrderOutMergeParam", description = "出库合并参数")
public class OrderOutMergeParam implements Serializable {
    @ApiModelProperty("库位ID")
    private Long locId;
    @ApiModelProperty("库位号")
    private String locNo;
    @ApiModelProperty("库位明细ID")
    private Long locDetlId;
    @ApiModelProperty("物料码")
    private String matnr;
    @ApiModelProperty("批次")
    private String batch;
    @ApiModelProperty("执行数量")
    private Long workQty;
    @ApiModelProperty("库位类型")
    private Long typeId;
    @ApiModelProperty("数量")
    private Double anfme;
    @ApiModelProperty("站点")
    private Long operationPort;
    private List<FieldParam> fieldParams;
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/entity/param/StockOutParam.java
New file
@@ -0,0 +1,30 @@
package com.zy.asrs.wms.asrs.entity.param;
import com.zy.asrs.wms.asrs.entity.LocDetl;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
 * Created by vincent on 2020/6/13
 */
@Data
@Accessors(chain = true)
@ApiModel(value = "StockOutParam", description = "手动出库参数")
public class StockOutParam {
    // 出站口
    @ApiModelProperty("出站口")
    private Integer outSite;
    @ApiModelProperty("出库类型: 1.拣货单, 2. 任务档 ")
    private Integer outType;
    // 物料编号集合
    @ApiModelProperty("库位明细集合")
    private List<LocDetl> locDetls;
}
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/manage/OutManage.java
@@ -1,8 +1,10 @@
package com.zy.asrs.wms.asrs.manage;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mysql.cj.util.StringUtils;
import com.zy.asrs.wms.asrs.entity.param.StockOutParam;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wms.asrs.entity.*;
import com.zy.asrs.wms.asrs.entity.dto.*;
@@ -1051,7 +1053,7 @@
        }
        if (!tucOrders.isEmpty()) {
            //CTU出库
            outStockByTUC(tucOrders, wave);
            outStockByCTU(tucOrders, wave);
        }
    }
@@ -1061,7 +1063,7 @@
     * @param tucOrders
     * @param wave
     */
    private void outStockByTUC(List<OrderOutMergeParam> tucOrders, Wave wave) {
    private void outStockByCTU(List<OrderOutMergeParam> tucOrders, Wave wave) {
        Map<Long, List<OrderOutMergeDto>> map = checkLoc(tucOrders, wave);
        for (Map.Entry<Long, List<OrderOutMergeDto>> entry : map.entrySet()) {
@@ -1152,7 +1154,7 @@
            }
            List<CacheSite> cacheSites = cacheSiteService.list(new LambdaQueryWrapper<CacheSite>()
                    .eq(CacheSite::getSiteStatus, CacheSiteStatusType.O.id).eq(CacheSite::getChannel,task.getTargetSite()));
                    .eq(CacheSite::getSiteStatus, CacheSiteStatusType.O.id).eq(CacheSite::getChannel, task.getTargetSite()));
            if (cacheSites.isEmpty()) {
                throw new CoolException("缓存站空间不足,请稍后再试");
@@ -1217,6 +1219,18 @@
        //根据库位ID分组
        Map<Long, List<OrderOutMergeParam>> listMap = flatOrders.stream().collect(Collectors.groupingBy(OrderOutMergeParam::getLocId));
        genPickSheet(listMap, pickSheet);
    }
    /**
     * @author Ryan
     * @date 2025/7/4
     * @description: 生面拣货明细
     * @version 1.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void genPickSheet(Map<Long, List<OrderOutMergeParam>> listMap, PickSheet pickSheet) {
        //生成拣货明细
        listMap.keySet().forEach(key -> {
            Loc curLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getId, key));
@@ -1246,11 +1260,14 @@
                if (!locDetlService.updateById(locDetl)) {
                    throw new CoolException("库存加锁失败!!");
                }
            });
        });
            curLoc.setLocStsId(LocStsType.R.val());
            if (!locService.updateById(curLoc)) {
                throw new CoolException("库位状态修改失败!!");
            }
        });
    }
    /**
@@ -1270,24 +1287,19 @@
        if (param == null) {
            throw new CoolException("参数不能为空");
        }
        List<String> channels = param.getChannels();
        if (channels == null) {
            throw new CoolException("通道参数不能为空");
        }
        if (channels.isEmpty()) {
            throw new CoolException("通道参数不能为空");
        }
//        for (String channel : channels) {
//            long count = cacheSiteService.count(new LambdaQueryWrapper<CacheSite>().eq(CacheSite::getChannel, channel).ne(CacheSite::getSiteStatus, 0));
//            if (count > 0) {
//                throw new CoolException(channel + "通道已经分配波次");
//            }
//        }
        List<Long> orderIds = param.getOrderIds();
        if (orderIds == null) {
            throw new CoolException("订单参数不能为空");
@@ -1448,7 +1460,190 @@
            waveService.removeById(wave.getId());
        }
    }
    /**
     * @author Ryan
     * @date 2025/7/3
     * @description: 库存出库信息
     * @version 1.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void outLocStock(StockOutParam param) {
        System.out.println(JSONObject.toJSONString(param));
        if (param.getOutType().equals(1)) {
            //拣货单
            outFlatStock(param);
        } else {
            //生成任务档
            generateTask(param);
        }
    }
    /**
     * @author Ryan
     * @param: []
     * @return: void
     * @date: 2025/7/4
     * @description: 手动出库生成任务
     */
    @Transactional(rollbackFor = Exception.class)
    public void generateTask(StockOutParam outParam) {
        for (LocDetl outLocDetl : outParam.getLocDetls()) {
            List<OrderOutMergeDto> orders = new ArrayList<>();
            LocDetl detl = locDetlService.getOne(new LambdaQueryWrapper<LocDetl>().eq(LocDetl::getId, outLocDetl.getId()));
            if (Objects.isNull(detl)) {
                continue;
            }
            OrderOutMergeDto outMergeParam = new OrderOutMergeDto();
            outMergeParam.setAnfme(outLocDetl.getAnfme())
                    .setLocNo(outLocDetl.getLocNo())
                    .setLocDetlId(detl.getId())
                    .setLocId(detl.getLocId())
                    .setMatnr(outLocDetl.getMatnr())
                    .setBatch(detl.getBatch());
            orders.add(outMergeParam);
            //根据库位ID分组
            Map<Long, List<OrderOutMergeDto>> listMap = orders.stream().collect(Collectors.groupingBy(OrderOutMergeDto::getLocId));
            //生成拣货单明细
            for (Map.Entry<Long, List<OrderOutMergeDto>> entry : listMap.entrySet()) {
                Long locId = entry.getKey();
                List<OrderOutMergeDto> list = entry.getValue();
                //判断是否全仓出库
                Boolean all = outUtils.isAllForMerge(locId, list);
                OrderOutMergeDto param = list.stream().findFirst().get();
                List<CacheSite> sites = cacheSiteService.list(new LambdaQueryWrapper<CacheSite>()
                        .isNotNull(CacheSite::getOrderId));
                if (!sites.isEmpty()) {
                    Map<String, Long> longMap = sites.stream().collect(Collectors.groupingBy(CacheSite::getChannel, Collectors.counting()));
                    Map.Entry<String, Long> entry1 = longMap.entrySet().stream().min(Map.Entry.comparingByValue()).get();
                    param.setPortSite(entry1.getKey());
                } else {
                    CacheSite cacheSite = cacheSiteService.getOne(new LambdaQueryWrapper<CacheSite>().last("limit 1"));
                    param.setPortSite(cacheSite.getChannel());
                }
                Loc loc = locService.getById(locId);
                if (loc == null) {
                    throw new CoolException("库位不存在");
                }
                if (!loc.getLocStsId().equals(LocStsType.F.val())) {
                    throw new CoolException(loc.getLocNo() + "库位状态异常");
                }
                OperationPort operationPort = operationPortService
                        .getOne(new LambdaQueryWrapper<OperationPort>()
                                .eq(OperationPort::getFlag, param.getPortSite()));
                if (Objects.isNull(operationPort)) {
                    throw new CoolException("作业口不存在");
                }
                //101 全拖出库   103 拣货出库
                long taskType = all ? 101L : 103L;
                Task task = new Task();
                task.setTaskNo(workService.generateTaskNo(taskType));
                task.setTaskSts(TaskStsType.GENERATE_OUT.id);
                task.setTaskType(taskType);
                task.setIoPri(workService.generateIoPri(taskType));
                task.setOriginLoc(loc.getLocNo());
                task.setTargetSite(operationPort.getFlag());
                task.setBarcode(loc.getBarcode());
                boolean res = taskService.save(task);
                if (!res) {
                    throw new CoolException("保存工作档失败");
                }
                for (OrderOutMergeDto merge : list) {
                    LocDetl locDetl = locDetlService.getById(merge.getLocDetlId());
                    if (locDetl == null) {
                        throw new CoolException("明细不存在");
                    }
                    TaskDetl taskDetl = new TaskDetl();
                    taskDetl.sync(locDetl);
                    taskDetl.setId(null);
                    taskDetl.setTaskId(task.getId());
                    taskDetl.setTaskNo(task.getTaskNo());
                    taskDetl.setAnfme(merge.getAnfme());
                    taskDetl.setStock(locDetl.getAnfme());
                    taskDetl.setOrderId(null);
                    taskDetl.setOrderNo(null);
                    if (!taskDetlService.save(taskDetl)) {
                        throw new CoolException("保存工作档明细失败");
                    }
                    List<LocDetlField> locDetlFields = locDetlFieldService
                            .list(new LambdaQueryWrapper<LocDetlField>()
                                    .eq(LocDetlField::getDetlId, locDetl.getId()));
                    for (LocDetlField locDetlField : locDetlFields) {
                        TaskDetlField taskDetlField = new TaskDetlField();
                        taskDetlField.sync(locDetlField);
                        taskDetlField.setId(null);
                        taskDetlField.setDetlId(taskDetl.getId());
                        boolean taskDetlFieldSave = taskDetlFieldService.save(taskDetlField);
                        if (!taskDetlFieldSave) {
                            throw new CoolException("明细扩展生成失败");
                        }
                    }
                }
                //库位F => R
                loc.setLocStsId(LocStsType.R.val());
                loc.setUpdateTime(new Date());
                boolean locUpdate = locService.updateById(loc);
                if (!locUpdate) {
                    throw new CoolException("库位状态更新失败");
                }
            }
        }
    }
    /**
     * @author Ryan
     * @date 2025/7/4
     * @description: 平库库存出库
     * @version 1.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void outFlatStock(StockOutParam param) {
        //生成拣货单
        PickSheet pickSheet = new PickSheet();
        //波次数量汇总
        Double sum = param.getLocDetls().stream().mapToDouble(LocDetl::getAnfme).sum();
        //生成拣货单号
        String pickNo = generatePickNO();
        pickSheet.setId(null)
                .setPickNo(pickNo)
                .setMemo("库存出库")
                .setAnfme(sum)
                .setType(2);
        if (!pickSheetService.save(pickSheet)) {
            throw new CoolException("拣货单写入失败!!");
        }
        for (LocDetl locDetl : param.getLocDetls()) {
            List<OrderOutMergeParam> orders = new ArrayList<>();
            LocDetl detl = locDetlService.getOne(new LambdaQueryWrapper<LocDetl>().eq(LocDetl::getLocNo, locDetl.getLocNo()));
            if (Objects.isNull(detl)) {
                continue;
            }
            OrderOutMergeParam outMergeParam = new OrderOutMergeParam();
            outMergeParam.setAnfme(locDetl.getAnfme())
                    .setLocNo(locDetl.getLocNo())
                    .setLocId(detl.getLocId())
                    .setLocDetlId(detl.getId())
                    .setMatnr(locDetl.getMatnr())
                    .setBatch(detl.getBatch());
            orders.add(outMergeParam);
            //根据库位ID分组
            Map<Long, List<OrderOutMergeParam>> listMap = orders.stream().collect(Collectors.groupingBy(OrderOutMergeParam::getLocId));
            //生成拣货单明细
            genPickSheet(listMap, pickSheet);
        }
    }
}
zy-asrs-wms/src/main/java/com/zy/asrs/wms/asrs/service/impl/PickSheetServiceImpl.java
@@ -38,7 +38,7 @@
            lambdaQueryWrapper.eq(!StringUtils.isNullOrEmpty(param.get("pickNo").toString()),PickSheet::getPickNo, param.get("pickNo"))
                    .eq(!StringUtils.isNullOrEmpty(param.get("waveNo").toString()), PickSheet::getWaveNo, param.get("waveNo"));
        }
        return this.baseMapper.selectMapsPage(params.getPage(), lambdaQueryWrapper);
        return this.baseMapper.selectPage(params.getPage(), lambdaQueryWrapper);
    }
    /**