zjj
2025-06-11 ffc423dc5ab1318370ea8e3b63e9213172f805bb
Merge remote-tracking branch 'origin/devlop' into devlop
26个文件已修改
1个文件已添加
842 ■■■■ 已修改文件
rsf-admin/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/asnOrder/AsnWareModal.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/delivery/DeliveryList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutStockPublic.jsx 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/McpController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/constant/Constants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/LocToTaskParams.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/OutStockToTaskParams.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/DeliveryTemplate.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/OutStockTemplate.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 161 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/.env
@@ -1,3 +1,3 @@
VITE_BASE_IP=192.168.4.50
VITE_BASE_IP=127.0.0.1
# VITE_BASE_IP=47.76.147.249
VITE_BASE_PORT=8080
rsf-admin/src/i18n/en.js
@@ -1168,7 +1168,7 @@
        recover: "recover",
        createWave: "Create Wave",
        order: 'Orders',
        modiftySite: 'Modify SiteNo',
    },
    request: {
        error: {
rsf-admin/src/i18n/zh.js
@@ -1171,6 +1171,7 @@
        publicWorking: '下发执行',
        createWave: "生成波次",
        recover: "继续收货",
        modiftySite: '修改库口',
    },
    request: {
        error: {
rsf-admin/src/page/orders/asnOrder/AsnWareModal.jsx
@@ -20,11 +20,9 @@
import TreeSelectInput from "@/page/components/TreeSelectInput";
const AsnWareModal = (props) => {
    const { open, setOpen, data, setData } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const refresh = useRefresh();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
@@ -55,7 +53,6 @@
        const hasarr = data.map(el => +el.matnrId)
        const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id)));
        const value = selectedData.map((el => {
            console.log(el);
            const dynamicFields = dyFields.reduce((acc, item) => {
                acc[item.fields] = el['extendFields']?.[item.fields] || '';
                return acc;
rsf-admin/src/page/orders/delivery/DeliveryList.jsx
@@ -122,7 +122,7 @@
                    rowClick={(id, resource, record) => false}
                    expand={false}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'platId', 'platCode', 'memo', 'startTime', 'endTime', 'updateBy', 'createTime']}
                    omit={['id', 'createTime', 'createBy', 'platId', 'platCode', 'memo', 'startTime','qty', 'endTime', 'updateBy', 'createTime']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.delivery.code" />
@@ -131,7 +131,7 @@
                    <TextField source="wkType$" label="table.field.delivery.wkType" />
                    <TextField source="source" label="table.field.delivery.source" />
                    <NumberField source="anfme" label="table.field.delivery.anfme" />
                    <NumberField source="workQty" label="table.field.delivery.workQty" />
                    <NumberField source="workQty" label="table.field.delivery.qty" />
                    <NumberField source="qty" label="table.field.delivery.qty" />
                    <TextField source="platCode" label="table.field.delivery.platCode" />
                    <DateField source="startTime" label="table.field.delivery.startTime" showTime />
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -5,6 +5,7 @@
  DatagridConfigurable,
  SearchInput,
  TopToolbar,
  Toolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
@@ -140,7 +141,7 @@
            <CreateByOrderButton setCreateDialog={setCreateDialog} />
            <MyCreateButton onClick={() => { setManualDialog(true); setmodalType(0) }} />
            <SelectColumnsButton preferenceKey='outStock' />
            <ImportButton value={'outStock'} />
            <ImportButton value={'outStockItem'} />
            {/* <MyExportButton /> */}
          </TopToolbar>
        )}
@@ -298,6 +299,6 @@
  }
  return (
    <ConfirmButton label={"toolbar.publicWorking"} startIcon={<AddTaskIcon />} onConfirm={taskEvent} size={"small"} />
    record.workQty < record.anfme ? <ConfirmButton label={"toolbar.publicWorking"} startIcon={<AddTaskIcon />} onConfirm={taskEvent} size={"small"} /> : <></>
  )
}
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx
@@ -5,6 +5,7 @@
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    Toolbar,
    Button,
    SelectColumnsButton,
    EditButton,
@@ -13,7 +14,6 @@
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    Toolbar,
    useRecordContext,
    useTranslate,
    useNotify,
@@ -45,7 +45,6 @@
import { Grid, margin, Stack, width } from "@mui/system";
import request from '@/utils/request';
import SaveIcon from '@mui/icons-material/Save';
import debounce from "lodash/debounce";
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
rsf-admin/src/page/orders/outStock/OutStockPublic.jsx
@@ -1,4 +1,4 @@
import { Box, Card, Grid, LinearProgress, Select, MenuItem, ListItemText, Tooltip } from "@mui/material";
import { Box, Card, Grid, LinearProgress, Select, MenuItem, ListItemText } from "@mui/material";
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    List,
@@ -13,7 +13,6 @@
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    Toolbar,
    useRecordContext,
    useTranslate,
    useNotify,
@@ -36,14 +35,18 @@
    SimpleForm,
    required,
    Form,
    useRefresh,
    useRedirect,
} from 'react-admin';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
import { styled } from '@mui/material/styles';
import { DataGrid, useGridApiContext } from '@mui/x-data-grid';
import { DataGrid, useGridApiContext, GridActionsCellItem, useGridApiRef } from '@mui/x-data-grid';
import request from '@/utils/request';
import ConfirmationNumberOutlinedIcon from '@mui/icons-material/ConfirmationNumberOutlined';
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
import ConfirmButton from '../../components/ConfirmButton';
import { Delete, Edit, Add } from '@mui/icons-material';
import OutStockSiteDialog from "./OutStockSiteDialog";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -60,17 +63,39 @@
const OutStockPublic = (props) => {
    const { record, open, setOpen } = props;
    const dataGridRef = useRef(null);
    const notify = useNotify();
    const gridRef = useGridApiRef();
    const [rows, setRows] = useState({});
    const translate = useTranslate();
    const [selectedIds, setSelectedIds] = React.useState([]);
    const [formData, setFormData] = useState({
        orderId: record?.id
    });
    const [dialog, setDialog] = useState(false);
    const [selectedValue, setSelectedValue] = useState({});
    const handleClickOpen = () => {
        setDialog(true);
    };
    const handleClose = (value) => {
        setDialog(false);
        setSelectedValue(value);
        console.log(rows);
        console.log(value);
           const newRows = rows.map(item => {
                return {
                    ...item,
                    siteNo: value?.site
                }
            })
            setRows(newRows);
    };
    useEffect(() => {
        getWaveRule()
    }, [formData, open])
    const getWaveRule = async () => {
        if (formData.waveId == null && formData.waveId == undefined) {
@@ -91,11 +116,13 @@
            [name]: value
        }));
    };
    return (
        <>
            <Box>
                <Grid sx={{ display: "flex" }} container rowSpacing={2} columnSpacing={2}>
                    <Grid item xl={6} gap={2} >
                    <Grid item xl={5.7} gap={2} >
                        <Card>
                            <Form>
                                <ReferenceInput
@@ -124,9 +151,7 @@
                                empty={false}
                                filter={{ asnId: record?.id, deleted: 0 }}
                                sort={{ field: "create_time", order: "desc" }}
                                actions={(
                                    <SelectColumnsButton preferenceKey='outStock' />
                                )}
                                actions={false}
                                perPage={DEFAULT_ITEM_PAGE_SIZE}
                            >
                                <LinearProgress
@@ -152,47 +177,213 @@
                            </List>
                        </Card>
                    </Grid>
                    <Grid item xl={6} gap={2}>
                    <Grid item xl={6.3} gap={2}>
                        <Card>
                            <Box>
                                <DataGrid
                            <Box sx={{ height: 500, width: '100%' }}>
                                <PreviewTable
                                    rows={rows}
                                    columns={columns}
                                    initialState={{
                                        pagination: {
                                            paginationModel: {
                                                pageSize: 15,
                                            },
                                        },
                                    }}
                                    ref={dataGridRef}
                                    checkboxSelection
                                    onRowSelectionModelChange={(ids) => {
                                        setSelectedIds(ids)
                                    }}
                                    pageSizeOptions={[15, 25, 35, 45]}
                                    gridRef={gridRef}
                                    setRows={setRows}
                                    record={record}
                                    selectedIds={selectedIds}
                                    setDialog={setDialog}
                                    setSelectedIds={setSelectedIds}
                                />
                            </Box>
                            <Box sx={{ textAlign: 'center' }}>
                                <CloseButton setOpen={setOpen} />
                                <SubmitButton selectedIds={selectedIds} setSelectedIds={setSelectedIds} />
                                <SubmitButton selectedIds={selectedIds} setSelectedIds={setSelectedIds} gridRef={gridRef} record={record} />
                            </Box>
                        </Card>
                    </Grid>
                </Grid>
                <Grid>
                    <OutStockSiteDialog
                        selectedValue={selectedValue}
                        open={dialog}
                        onClose={handleClose}
                    />
                </Grid>
            </Box>
        </>
    );
}
const PreviewTable = ({ rows, gridRef, setRows, record, selectedIds, setSelectedIds, setDialog }) => {
    gridRef.current = useGridApiRef();
//提交按钮
const SubmitButton = ({ selectedIds, setSelectedIds }) => {
    const submit = () => {
        if (selectedIds.length < 1) { return }
        console.log(selectedIds);
    const columns = [
        { field: 'id', headerName: 'ID', width: 40 },
        { field: 'locCode', headerName: '库位', width: 110 },
        { field: 'barcode', headerName: '容器', width: 120 },
        { field: 'batch', headerName: '批次', width: 90 },
        { field: 'unit', headerName: '单位', width: 90 },
        { field: 'outQty', headerName: '本次出库数量', width: 110 },
        {
            field: 'siteNo',
            headerName: '出库口',
            width: 90,
            type: 'singleSelect',
            editable: true,
            renderCell: (params) => (
                <OutStockSiteNo value={params.value} />
            ),
            renderEditCell: (params) => (
                <OutStockSite {...params} />
            ),
        },
        {
            field: 'actions',
            type: 'actions',
            headerName: '操作',
            with: 120,
            getActions: (params) => [
                <GridActionsCellItem
                    icon={<Delete />}
                    label="Delete"
                    onClick={() => handleDelete(params.row, rows, setRows)}
                />,
            ]
        },
    ]
    /**
     * 删除事件
     * @param {*} params
     */
    const handleDelete = (params, rows, setRows) => {
        const outRows = rows.filter(row => {
            return row.id !== params.id
        })
        setRows(outRows)
    }
    const OutStockSiteNo = React.memo(function OutStockSiteNo(props) {
        const { value } = props;
        if (!value) {
            return null;
        }
        return (
            <Box
                sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
            >
                <span>{value}</span>
            </Box>
        );
    });
    const CustomToolBar = () => {
        const selectSiteNo = () => {
            setDialog(true)
        }
        return (
            selectedIds.length > 0 ? <Box sx={{
                p: 1,
                display: 'flex',
                justifyContent: 'flex-end',
                borderTop: '1px solid rgba(224, 224, 224, 1)'
            }}>
                <Button
                    onClick={selectSiteNo}
                    variant="outlined"
                    label="toolbar.modiftySite"
                    size="medium"
                    sx={{ mr: 1 }} />
            </Box> : <></>
        );
    }
    const OutStockSite = (params) => {
        const { id, field, siteNo, row: { staNos } } = params;
        const apiRef = useGridApiContext();
        const handleChange = async (event) => {
            await apiRef.current.setEditCellValue(
                { id, field, value: event.target.value },
                event,
            );
            apiRef.current.stopCellEditMode({ id, field });
        };
        const handleClose = (event, reason) => {
            if (reason === 'backdropClick') {
                apiRef.current.stopCellEditMode({ id, field });
            }
        };
        return (
            <Select
                value={siteNo}
                onChange={handleChange}
                MenuProps={{
                    onClose: handleClose,
                }}
                sx={{
                    height: '100%',
                    '& .MuiSelect-select': {
                        display: 'flex',
                        alignItems: 'center',
                        pl: 1,
                    },
                }}
                autoFocus
                fullWidth
                open
            >
                {staNos.map((option) => {
                    return (
                        <MenuItem
                            key={option}
                            value={option.staNo}
                        >
                            <ListItemText sx={{ overflow: 'hidden' }} primary={option.staNo} />
                        </MenuItem>
                    );
                })}
            </Select >
        )
    }
    return (
        <DataGrid
            rows={rows}
            columns={columns}
            slots={{ toolbar: CustomToolBar }}
            apiRef={gridRef}
            initialState={{
                pagination: {
                    paginationModel: {
                        pageSize: 15,
                    },
                },
            }}
            checkboxSelection
            pageSizeOptions={[15, 25, 35, 45]}
            onRowSelectionModelChange={(ids) => {
                setSelectedIds(ids)
            }}
        />
    )
}
//提交按钮
const SubmitButton = ({ selectedIds, setSelectedIds, gridRef, record }) => {
    const notify = useNotify();
    const refresh = useRefresh();
    const redirect = useRedirect();
    const submit = async () => {
        console.log(record);
        const items = gridRef.current?.getSortedRows();
        const { data: { code, data, msg } } = await request.post('/outStock/generate/tasks', { items, outId: record?.id });
        if (code == 200) {
            refresh();
            redirect("/task")
        } else {
            notify(msg);
        }
    }
    return (
        <ConfirmButton
            label="toolbar.confirm"
@@ -219,95 +410,6 @@
            sx={{ margin: '3.5em' }} />
    )
}
const columns = [
    { field: 'id', headerName: 'ID', width: 40 },
    { field: 'locCode', headerName: '库位', width: 110 },
    { field: 'barcode', headerName: '容器', width: 120 },
    { field: 'batch', headerName: '批次', width: 90 },
    { field: 'unit', headerName: '单位', width: 90 },
    { field: 'workQty', headerName: '本次出库数量', width: 110 },
    {
        field: 'siteNo',
        headerName: '出库口',
        width: 90,
        type: 'singleSelect',
        editable: true,
        renderCell: (params) => (
            <OutStockSiteNo value={params.value} />
        ),
        renderEditCell: (params) => (
            <OutStockSite {...params} />
        ),
    },
]
const OutStockSiteNo = React.memo(function OutStockSiteNo(props) {
    const { value } = props;
    if (!value) {
        return null;
    }
    return (
        <Box
            sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
        >
            <span>{value}</span>
        </Box>
    );
});
const OutStockSite = (params) => {
    const { id, field, siteNo, row: { staNos } } = params;
    const apiRef = useGridApiContext();
    const handleChange = async (event) => {
        await apiRef.current.setEditCellValue(
            { id, field, value: event.target.value },
            event,
        );
        apiRef.current.stopCellEditMode({ id, field });
    };
    const handleClose = (event, reason) => {
        if (reason === 'backdropClick') {
            apiRef.current.stopCellEditMode({ id, field });
        }
    };
    return (
        <Select
            value={siteNo}
            onChange={handleChange}
            MenuProps={{
                onClose: handleClose,
            }}
            sx={{
                height: '100%',
                '& .MuiSelect-select': {
                    display: 'flex',
                    alignItems: 'center',
                    pl: 1,
                },
            }}
            autoFocus
            fullWidth
            open
        >
            {staNos.map((option) => {
                return (
                    <MenuItem
                        key={option}
                        value={option.staNo}
                    >
                        <ListItemText sx={{ overflow: 'hidden' }} primary={option.staNo} />
                    </MenuItem>
                );
            })}
        </Select >
    )
}
export default OutStockPublic;
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/McpController.java
@@ -84,10 +84,15 @@
        if (Objects.isNull(param)) {
            return R.error("参数不能为空!!");
        }
        return R.ok(param);
//        param.setType(Constants.TASK_TYPE_OUT_STOCK);
//
//        return locItemService.generateTask(param, getLoginUserId());
        param.setType(Constants.TASK_TYPE_OUT_STOCK);
        try {
            locItemService.generateTask(param, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("任务生成成功");
    }
rsf-server/src/main/java/com/vincent/rsf/server/common/constant/Constants.java
@@ -103,6 +103,11 @@
    public static final String TASK_TYPE_OUT_CHECK = "check";
    /**
     * 单据出库
     */
    public static final String TASK_TYPE_ORDER_OUT_STOCK = "OrderOutStock";
    /**
     * 拣料出库
     */
    public static final String TASK_TYPE_OUT_PICK = "pick";
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
@@ -97,7 +97,13 @@
        }
        param.setType(Constants.TASK_TYPE_OUT_STOCK);
        return locItemService.generateTask(param, getLoginUserId());
        try {
            locItemService.generateTask(param, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok();
    }
    /**
@@ -129,7 +135,13 @@
        }
        map.setType(Constants.TASK_TYPE_OUT_CHECK);
        return R.ok("任务生成成功").add(locItemService.generateTask(map, getLoginUserId()));
        try {
            locItemService.generateTask(map, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("任务生成成功");
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -8,6 +9,8 @@
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.entity.excel.OutStockTemplate;
import com.vincent.rsf.server.manager.enums.OrderType;
import com.vincent.rsf.server.manager.enums.OrderWorkType;
import com.vincent.rsf.server.common.annotation.OperationLog;
@@ -28,6 +31,8 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -38,6 +43,8 @@
@RestController
@Api(tags = "出库单据")
public class OutStockController extends BaseController {
    Logger logger = LoggerFactory.getLogger(OutStockController.class);
    @Autowired
    private OutStockService outStockService;
@@ -234,12 +241,63 @@
    }
    @PostMapping("/outStock/order/getOutTaskItems")
    @ApiOperation("出库单库位预览")
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    public R getOrderOutTaskItem(@RequestBody OrderOutTaskParam param){
        if (Cools.isEmpty(param)){
    public R getOrderOutTaskItem(@RequestBody OrderOutTaskParam param) {
        if (Cools.isEmpty(param)) {
            return R.error("参数不能为空");
        }
        return outStockService.getOrderOutTaskItem(param);
    }
    /**
     * 出库单生成出库任务
     * @param params
     * @return
     */
    @PostMapping("/outStock/generate/tasks")
    @ApiOperation("出库单生成出库任务")
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    public R genOutStockTask( @RequestBody Map<String, Object> params) {
        if (Cools.isEmpty()) {
            return R.error("参数不能为空!!");
        }
        if (Objects.isNull(params.get("outId"))) {
            return R.error("出库参数不能为空!!");
        }
        List<OutStockToTaskParams> taskParams = JSONArray.parseArray(JSONArray.toJSONString(params.get("items")), OutStockToTaskParams.class);
        return  outStockService.genOutStockTask(taskParams, getLoginUserId(), Long.parseLong(params.get("outId").toString()));
    }
    /**
     * 获取出库站点
     * @return
     */
    @GetMapping("/outStock/tasks/sites")
    @ApiOperation("获取出库库口")
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    public R getSiteNos() {
        return outStockService.getSiteNos();
    }
    /**
     * @author Ryan
     * @description 下载模板
     * @param
     * @return
     * @time 2025/4/18 08:17
     */
    @PostMapping("/outStock/template/download")
    @ApiOperation("下载收货单模板")
    @PreAuthorize("hasAuthority('manager:outStockItem:update')")
    public void downloadTemplate(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        OutStockTemplate template = ExcelUtil.mockData(OutStockTemplate.class);
        List<OutStockTemplate> list = Arrays.asList(template);
        ExcelUtil.build(ExcelUtil.create(list, OutStockTemplate.class, true), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java
@@ -159,7 +159,7 @@
    @PostMapping("/outStockItem/import")
    @ApiOperation("ASN导入接口")
    @PreAuthorize("hasAuthority('manager:outStockItem:update')")
    public R importExcel(@RequestParam(value = "file") MultipartFile file, @RequestParam String asnId) throws Exception {
    public R importExcel(@RequestParam(value = "file") MultipartFile file) throws Exception {
        if (Objects.isNull(file)) {
            R.error("文件不能为空!!");
        }
@@ -167,20 +167,6 @@
        return outStockItemService.excelImport(file, hashMap, getLoginUserId());
    }
    /**
     * @author Ryan
     * @description 下载模板
     * @param
     * @return
     * @time 2025/4/18 08:17
     */
    @PostMapping("/outStockItem/template/download")
    @ApiOperation("下载收货单模板")
    @PreAuthorize("hasAuthority('manager:outStockItem:update')")
    public void downloadTemplate(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        AsnOrderTemplate template = ExcelUtil.mockData(AsnOrderTemplate.class);
        List<AsnOrderTemplate> list = Arrays.asList(template);
        ExcelUtil.build(ExcelUtil.create(list, AsnOrderTemplate.class, true), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/LocToTaskParams.java
@@ -17,6 +17,9 @@
    @ApiModelProperty("类型: check:盘点出库, outStock: 库存出库")
    private String type;
    @ApiModelProperty("原单据ID (用户单据出库查找业务类型")
    private Long sourceId;
    @ApiModelProperty("目标站点")
    private String siteNo;
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/OutStockToTaskParams.java
New file
@@ -0,0 +1,47 @@
package com.vincent.rsf.server.manager.controller.params;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map;
@Data
@Accessors(chain = true)
@ApiModel(value = "OutStockToTaskParams", description = "出库单生成任务参数")
public class OutStockToTaskParams {
    @ApiModelProperty("库位")
    private String locCode;
    @ApiModelProperty("库位明细ID")
    private Long id;
    @ApiModelProperty("托盘码")
    private String barcode;
    @ApiModelProperty("批次")
    private String batch;
    @ApiModelProperty("单位")
    private String unit;
    @ApiModelProperty("客单号")
    private String platOrderCode;
    @ApiModelProperty("字段索引")
    private String fieldsIndex;
    @ApiModelProperty("扩展字段")
    @TableField(exist = false)
    private Map<String, String> extendFields;
    @ApiModelProperty("数量")
    private Double outQty;
    @ApiModelProperty("站点")
    private String siteNo;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
@@ -172,6 +172,10 @@
    @TableField(exist = false)
    private Map<String, String> extendFields;
    @ApiModelProperty("托盘码")
    @TableField(exist = false)
    private String barcode;
    /**
     * 状态 1: 正常  0: 冻结  
     */
@@ -220,13 +224,13 @@
    private Date updateTime;
    @TableField(exist = false)
    private Long waveId;
    private Long sourceId;
    @TableField(exist = false)
    private Long waveItemId;
    private Long source;
    @TableField(exist = false)
    private String waveCode;
    private String sourceCode;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/DeliveryTemplate.java
@@ -29,7 +29,7 @@
    @Excel(name = "单据类型")
    @ApiModelProperty(value= "单据类型")
    @ExcelComment(value = "type", example = "入库单")
    @ExcelComment(value = "type", example = "出库单")
    private String type;
    @Excel(name = "单据来源")
@@ -54,7 +54,7 @@
    @Excel(name = "业务类型")
    @ApiModelProperty(value= "业务类型")
    @ExcelComment(value = "wkType", example = "采购入库单")
    @ExcelComment(value = "wkType", example = "销售出库单")
    private String wkType;
    @Excel(name = "项目名称")
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/OutStockTemplate.java
@@ -68,7 +68,7 @@
     */
    @Excel(name = "*单据类型")
    @ApiModelProperty(value = "*单据类型")
    @ExcelComment(value = "type", example = "入库单")
    @ExcelComment(value = "type", example = "出库单")
    private String type;
    /**
@@ -76,7 +76,7 @@
     */
    @Excel(name = "*业务类型")
    @ApiModelProperty(value = "*业务类型")
    @ExcelComment(value = "wkType", example = "采购入库单")
    @ExcelComment(value = "wkType", example = "销售出库单")
    private String wkType;
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java
@@ -26,9 +26,11 @@
    OUT_STOCK_STATUS_TASK_INIT("10", "初始化"),
    OUT_STOCK_STATUS_TASK_EXCE("11", "待处理"),
    OUT_STOCK_STATUS_TASK_WAVE("12", "生成波次"),
    OUT_STOCK_STATUS_TASK_WORKING("13", "作业中")
    ;
    OUT_STOCK_STATUS_TASK_WAVE("11", "生成波次"),
    OUT_STOCK_STATUS_TASK_CREATE("13", "生成工作档"),
    OUT_STOCK_STATUS_TASK_WORKING("14", "作业中")
            ;
    AsnExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java
@@ -8,7 +8,7 @@
public interface LocItemService extends IService<LocItem> {
    R generateTask(LocToTaskParams map, Long loginUserId);
    void generateTask(LocToTaskParams map, Long loginUserId) throws Exception;
    Task genMoveTask(LocToTaskParams map, Long loginUserId);
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -4,6 +4,7 @@
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.entity.AsnOrder;
import java.util.List;
@@ -21,4 +22,8 @@
    R updateOrderItem(AsnOrderAndItemsParams params, Long loginUserId);
    R getOrderOutTaskItem(OrderOutTaskParam param);
    R genOutStockTask(List<OutStockToTaskParams> params, Long loginUserId, Long outId);
    R getSiteNos();
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -7,7 +7,6 @@
import com.vincent.rsf.server.api.entity.dto.InTaskMsgDto;
import com.vincent.rsf.server.api.service.WcsService;
import com.vincent.rsf.server.api.utils.LocUtils;
import com.vincent.rsf.server.api.utils.SlaveProperties;
import com.vincent.rsf.server.common.constant.Constants;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.entity.*;
@@ -19,6 +18,8 @@
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -29,6 +30,8 @@
@Service("locItemService")
public class LocItemServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements LocItemService {
    Logger logger = LoggerFactory.getLogger(LocItemServiceImpl.class);
    @Autowired
    private LocService locService;
@@ -42,6 +45,8 @@
    private DeviceSiteService deviceSiteService;
    @Autowired
    private WcsService wcsService;
    @Autowired
    private OutStockService outStockService;
    /**
@@ -55,7 +60,7 @@
    @Override
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    public R generateTask(LocToTaskParams map, Long loginUserId) {
    public void generateTask(LocToTaskParams map, Long loginUserId) throws Exception {
        if (Objects.isNull(map.getSiteNo())) {
            throw new CoolException("站点不能为空!");
        }
@@ -66,19 +71,31 @@
        String siteNo = map.getSiteNo();
        List<LocItem> items = map.getItems();
        Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId));
        AsnOrder order;
        if (!Objects.isNull(map.getSourceId())) {
            order = outStockService.getById(map.getSourceId());
        } else {
            order = new AsnOrder();
        }
        listMap.keySet().forEach(key -> {
            Task task = new Task();
            Loc loc = locService.getById(key);
            logger.info("库位:>{}<UNK>", loc.getCode());
            if (Objects.isNull(loc)) {
                throw new CoolException("数据错误:所选库存信息不存在!!");
            }
            if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) {
                throw new CoolException("库位:" + loc.getCode() +  ",不处于F.在库状态,不可执行R.出库预约操作!!");
            }
            loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
            if (!locService.updateById(loc)) {
                throw new CoolException("库位状态更新失败!!");
            }
            Task moveTask = new Task();
            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
            task.setOrgLoc(loc.getCode())
                    .setTaskCode(ruleCode)
@@ -101,12 +118,12 @@
            List<LocItem> locItemList = listMap.get(key);
            Double outQty = locItemList.stream().mapToDouble(LocItem::getOutQty).sum();
            Task serviceOne = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, loc.getBarcode()));
            if (!Objects.isNull(serviceOne)) {
                throw new CoolException("托盘任务执行中,不能重复创建!");
            }
//            Task serviceOne = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, loc.getBarcode()));
//            if (!Objects.isNull(serviceOne)) {
//                throw new CoolException("托盘任务执行中,不能重复创建!");
//            }
            if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) {
            if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK) || map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) {
                Double useQty = Math.round((outQty + workQty) * 10000) / 10000.0;
                if (orgQty.compareTo(useQty) > 0) {
                    //拣料出库
@@ -140,6 +157,7 @@
                }
                task.setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type).setWarehType(deviceSite.getDevice());
            }
            if (!taskService.save(task)) {
                throw new CoolException("任务创建失败!!");
            }
@@ -186,13 +204,19 @@
                        .setBatch(item.getBatch())
                        .setUpdateBy(loginUserId)
                        .setCreateBy(loginUserId)
                        .setSource(item.getId())
                        .setSourceId(item.getLocId())
                        .setSourceCode(item.getLocCode())
                        .setCreateTime(new Date())
                        .setUpdateTime(new Date())
                        .setOrderType(OrderType.ORDER_OUT.type)
                        .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type));
               if (map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) {
                    taskItem.setWkType(Short.parseShort(order.getWkType()))
                            .setSourceCode(order.getCode())
                            .setSourceId(order.getId());
               } else if (map.getType().equals(Constants.TASK_TYPE_OUT_CHECK) || map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) {
                   taskItem.setSource(item.getId())
                           .setSourceId(item.getLocId())
                           .setSourceCode(item.getLocCode());
               }
                taskItems.add(taskItem);
                Double qty = Math.round((item.getWorkQty() + item.getOutQty()) * 10000) / 10000.0;
@@ -222,8 +246,6 @@
                throw new CoolException("任务明细生成失败!!");
            }
        });
        return R.ok("任务生成完成!!");
    }
    /**
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java
@@ -11,6 +11,7 @@
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.manager.entity.excel.OutStockTemplate;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.enums.OrderType;
import com.vincent.rsf.server.manager.enums.OrderWorkType;
import com.vincent.rsf.server.common.domain.BaseParam;
@@ -101,9 +102,6 @@
    public R excelImport(MultipartFile file, HashMap<String, Object> hashMap, Long loginUserId) throws Exception {
        ExcelImportResult result = ExcelImportUtil.importExcelMore(file.getInputStream(), OutStockTemplate.class, ExcelUtil.getDefaultImportParams());
        if (result.getList().isEmpty()) {
            throw new CoolException("物料导入失败!!");
        }
        if (result.getList().isEmpty()) {
            throw new CoolException("表格内容不能为空!!");
        }
        List<OutStockTemplate> resultList = result.getList();
@@ -111,7 +109,7 @@
        for (String key : listMap.keySet()) {
            OutStockTemplate template = listMap.get(key).stream().findFirst().get();
            AsnOrder asnOrder = outStockService.getOne(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getCode, template.getCode()));
            if (Objects.isNull(asnOrder)) {
            if (!Objects.isNull(asnOrder)) {
                continue;
            }
            AsnOrder order = new AsnOrder();
@@ -129,7 +127,7 @@
            for (OutStockTemplate orderTemplate : listMap.get(key)) {
                AsnOrderItem orderItem = new AsnOrderItem();
                Matnr matnr = null;
                if (!Objects.isNull(orderTemplate.getMatnrCode()) || StringUtils.isNotBlank(orderTemplate.getMatnrCode())) {
                if (StringUtils.isNotBlank(orderTemplate.getMatnrCode())) {
                    matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>()
                            .eq(Matnr::getCode, orderTemplate.getMatnrCode()));
                }
@@ -151,9 +149,10 @@
                }
            }
            if (!items.isEmpty()) {
                double qty = items.stream().mapToDouble(AsnOrderItem::getQty).sum();
                double purQty = items.stream().mapToDouble(AsnOrderItem::getPurQty).sum();
                if (!outStockService.update(new LambdaUpdateWrapper<AsnOrder>().set(AsnOrder::getQty, qty).set(AsnOrder::getAnfme, purQty).eq(AsnOrder::getId, order.getId()))) {
                double purQty = items.stream().mapToDouble(AsnOrderItem::getAnfme).sum();
                if (!outStockService.update(new LambdaUpdateWrapper<AsnOrder>()
                                .set(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                        .set(AsnOrder::getAnfme, purQty).eq(AsnOrder::getId, order.getId()))) {
                    throw new CoolException("单据数量修改失败!!");
                }
            }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -1,16 +1,18 @@
package com.vincent.rsf.server.manager.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.api.utils.LocUtils;
import com.vincent.rsf.server.common.constant.Constants;
import com.vincent.rsf.server.manager.controller.dto.ExistDto;
import com.vincent.rsf.server.manager.controller.dto.OrderOutItemDto;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.entity.*;
@@ -20,6 +22,8 @@
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -28,6 +32,7 @@
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import com.vincent.rsf.server.manager.enums.WaveRuleType;
/**
@@ -39,6 +44,9 @@
 */
@Service("outStockServiceImpl")
public class OutStockServiceImpl extends ServiceImpl<AsnOrderMapper, AsnOrder> implements OutStockService {
    public Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private AsnOrderItemService asnOrderItemService;
@@ -368,6 +376,7 @@
        }
        return R.ok();
    }
    /**
     * @param
     * @return
@@ -400,14 +409,14 @@
    @Override
    public R getOrderOutTaskItem(OrderOutTaskParam param) {
        if (Cools.isEmpty(param.getWaveId())){
        if (Cools.isEmpty(param.getWaveId())) {
            throw new CoolException("策略参数为空");
        }
        if (Cools.isEmpty(param.getOrderId())){
            throw new CoolException("单据id为空");
        if (Cools.isEmpty(param.getOrderId())) {
            throw new CoolException("单据ID为空");
        }
        WaveRule waveRule = waveRuleService.getOne(new LambdaQueryWrapper<WaveRule>().eq(WaveRule::getId, param.getWaveId()));
        if (Cools.isEmpty(waveRule)){
        if (Cools.isEmpty(waveRule)) {
            throw new CoolException("未找到当前策略");
        }
        List<OrderOutItemDto> locItems = null;
@@ -415,7 +424,108 @@
        return R.ok(locItems);
    }
    private List<LocItem> getEfficiencyFirstItemList(AsnOrderItem asnOrderItem){
    /**
     * 生成出库任务
     *
     * @param params
     * @param outId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R genOutStockTask(List<OutStockToTaskParams> params, Long loginUserId, Long outId) {
        if (params.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        //优先生成浅库位任务
        List<OutStockToTaskParams> Items = params.stream().sorted(Comparator.comparing(OutStockToTaskParams::getLocCode).thenComparing(item -> {
            return LocUtils.isShallowLoc(item.getLocCode()) ? 1 : 0;
        }).reversed()).collect(Collectors.toList());
        for (OutStockToTaskParams param : Items) {
            if (Objects.isNull(param)) {
                continue;
            }
            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, param.getBarcode()));
            if (!Objects.isNull(loc)) {
                List<LocItem> locItems = new ArrayList<>();
                LocItem locItem = locItemService.getById(param.getId());
                AsnOrderItem orderItem = outStockItemService.getOne(new LambdaQueryWrapper<AsnOrderItem>()
                        .eq(AsnOrderItem::getAsnId, outId)
                        .eq(StringUtils.isNotBlank(locItem.getBatch()), AsnOrderItem::getSplrBatch, locItem.getBatch())
                        .eq(StringUtils.isNotBlank(locItem.getFieldsIndex()), AsnOrderItem::getFieldsIndex, locItem.getFieldsIndex())
                        .eq(AsnOrderItem::getMatnrId, locItem.getMatnrId()));
                if (Objects.isNull(orderItem)) {
                    throw new CoolException("单据明细不存在!!");
                }
                locItem.setOutQty(param.getOutQty())
                        .setBatch(param.getBatch())
                        .setSourceId(outId)
                        .setSourceCode(orderItem.getAsnCode())
                        .setSource(orderItem.getId());
                locItems.add(locItem);
                LocToTaskParams taskParams = new LocToTaskParams();
                taskParams.setType(Constants.TASK_TYPE_ORDER_OUT_STOCK)
                        .setOrgLoc(loc.getCode())
                        .setItems(locItems)
                        .setSourceId(outId)
                        .setSiteNo(param.getSiteNo());
                try {
                    //生成出库任务
                    locItemService.generateTask(taskParams, loginUserId);
                } catch (Exception e) {
                    logger.error("UNK", e);
                    throw new CoolException(e.getMessage());
                }
                Double workQty = Math.round((orderItem.getWorkQty() + locItem.getOutQty()) * 10000) / 10000.0;
                orderItem.setUpdateBy(loginUserId).setUpdateTime(new Date()).setWorkQty(workQty);
                if (!outStockItemService.updateById(orderItem)) {
                    throw new CoolException("单据明细修改失败!!");
                }
            }
        }
        Double sum = Items.stream().mapToDouble(OutStockToTaskParams::getOutQty).sum();
        //更新出库单明细及主单
        AsnOrder outOrder = outStockService.getById(outId);
        if (Objects.isNull(outOrder)) {
            throw new CoolException("出库单据不存在!!");
        }
        Double workQty = Math.round((outOrder.getWorkQty() + sum) * 10000) / 10000.0;
        outOrder.setWorkQty(workQty).setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_CREATE.val);
        if (!outStockService.updateById(outOrder)) {
            throw new CoolException("出库单状态修改失败!!");
        }
        return R.ok();
    }
    /**
     * 获取出库站点
     * @return
     */
    @Override
    public R getSiteNos() {
        List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_MERGE_OUT.type,
                TaskType.TASK_TYPE_OUT.type,
                TaskType.TASK_TYPE_MERGE_OUT.type,
                TaskType.TASK_TYPE_PICK_AGAIN_OUT.type);
        List<DeviceSite> sites = deviceSiteService.list(new LambdaQueryWrapper<DeviceSite>().in(DeviceSite::getType, list).groupBy(DeviceSite::getSite));
        return R.ok(sites);
    }
    private List<LocItem> getEfficiencyFirstItemList(AsnOrderItem asnOrderItem) {
        LambdaQueryWrapper<LocItem> locItemQueryWrapper = new LambdaQueryWrapper<>();
        locItemQueryWrapper.eq(LocItem::getMatnrCode, asnOrderItem.getMatnrCode());
        locItemQueryWrapper.eq(LocItem::getBatch, asnOrderItem.getSplrBatch());
@@ -434,7 +544,7 @@
        return locItems;
    }
    private List<LocItem> getFirstInFirstOutItemList(AsnOrderItem asnOrderItem){
    private List<LocItem> getFirstInFirstOutItemList(AsnOrderItem asnOrderItem) {
        LambdaQueryWrapper<LocItem> locItemQueryWrapper = new LambdaQueryWrapper<>();
        locItemQueryWrapper.eq(LocItem::getMatnrCode, asnOrderItem.getMatnrCode());
        locItemQueryWrapper.eq(LocItem::getBatch, asnOrderItem.getSplrBatch());
@@ -451,10 +561,9 @@
        return locItems;
    }
    private List<OrderOutItemDto> getOutOrderList(Long orderId,WaveRule waveRule){
    private List<OrderOutItemDto> getOutOrderList(Long orderId, WaveRule waveRule) {
        List<AsnOrderItem> asnOrderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
                .eq(AsnOrderItem::getAsnId, orderId)
        );
                .eq(AsnOrderItem::getAsnId, orderId));
        List<OrderOutItemDto> list = new ArrayList<>();
        Set<ExistDto> existDtos = new HashSet<>();
@@ -470,16 +579,17 @@
                locItems = getEfficiencyFirstItemList(asnOrderItem);
            } else if (WaveRuleType.First_In_First_Out.type.equals(waveRule.getType())) {
                locItems = getFirstInFirstOutItemList(asnOrderItem);
            }else {
            } else {
                locItems = getFirstInFirstOutItemList(asnOrderItem);
            }
            for (LocItem locItem : locItems) {
                Loc loc = locService.getById(locItem.getLocId());
                List<LocItem> itemList = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocCode, locItem.getLocCode()));
                if (issued.doubleValue() > 0){
                if (issued.doubleValue() > 0) {
                    ExistDto existDto = new ExistDto().setBatch(locItem.getBatch()).setMatnr(locItem.getMatnrCode()).setLocNo(locItem.getLocCode());
                    if (existDtos.add(existDto)){
                        locItem.setWorkQty(issued.doubleValue() >= locItem.getAnfme() ? locItem.getAnfme() : issued.doubleValue());
                    if (existDtos.add(existDto)) {
                        locItem.setOutQty(issued.doubleValue() >= locItem.getAnfme() ? locItem.getAnfme() : issued.doubleValue());
                        locItem.setBarcode(loc.getBarcode());
                        OrderOutItemDto orderOutItemDto = new OrderOutItemDto();
                        orderOutItemDto.setLocItem(locItem);
@@ -487,21 +597,28 @@
                                .eq(DeviceSite::getChannel, loc.getChannel())
                                .eq(DeviceSite::getType, issued.doubleValue() >= locItem.getAnfme() && itemList.size() == 1 ? TaskType.TASK_TYPE_OUT.type : TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                        );
                        List<OrderOutItemDto.staListDto> maps = new ArrayList<>();
                        for (DeviceSite sta : deviceSites) {
                            OrderOutItemDto.staListDto staListDto = new OrderOutItemDto.staListDto();
                            staListDto.setStaNo(sta.getSite());
                            staListDto.setStaName(sta.getSite());
                            maps.add(staListDto);
                        if (!deviceSites.isEmpty()) {
                            List<OrderOutItemDto.staListDto> maps = new ArrayList<>();
                            for (DeviceSite sta : deviceSites) {
                                OrderOutItemDto.staListDto staListDto = new OrderOutItemDto.staListDto();
                                staListDto.setStaNo(sta.getSite());
                                staListDto.setStaName(sta.getSite());
                                maps.add(staListDto);
                            }
                            orderOutItemDto.setStaNos(maps);
                            //默认获取第一站点
                            DeviceSite deviceSite = deviceSites.stream().findFirst().get();
                            orderOutItemDto.setSiteNo(deviceSite.getSite());
                        }
                        orderOutItemDto.setStaNos(maps);
                        list.add(orderOutItemDto);
                        issued = issued.subtract(new BigDecimal(locItem.getAnfme().toString()));
                    }
                }
                } else {
                }
            }
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -79,6 +79,10 @@
    private LocItemWorkingService locItemWorkingService;
    @Autowired
    private WcsService wcsService;
    @Autowired
    private OutStockService outStockService;
    @Autowired
    private OutStockItemService outStockItemService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -519,6 +523,7 @@
    /**
     * 任务完成后,判断深库位是否为空,如果为空生成移库任务
     *
     * @param loginUserId
     * @param curLoc
     */
@@ -785,17 +790,47 @@
            if (!taskItems.isEmpty()) {
                for (TaskItem item : taskItems) {
                    if (item.getOrderType().equals(OrderType.ORDER_OUT.type)) {
                        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getOrgLoc()));
                        if (Objects.isNull(loc)) {
                            throw new CoolException("数据错误:库位信息不存在!!");
                        }
                        loc.setUseStatus(LocStsType.LOC_STS_TYPE_F.type).setBarcode(task.getBarcode()).setUpdateBy(loginUserId).setUpdateTime(new Date());
                        if (!locService.updateById(loc)) {
                            throw new CoolException("库位信息修改失败!!");
                        }
                        //出库
                        if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type))) {
                            //库存出库
                            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getId, item.getSourceId()));
                            if (Objects.isNull(loc)) {
                                throw new CoolException("数据错误:库位信息不存在!!");
                        } else if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_OTHER.type))) {
                            //其它出库
                        } else {
                            //出库单出库
                            AsnOrder asnOrder = outStockService.getById(item.getSourceId());
                            if (Objects.isNull(asnOrder)) {
                                throw new CoolException("数据错误:单据已不存在!!");
                            }
                            Double workQty = Math.round((asnOrder.getWorkQty() - item.getAnfme()) * 10000) / 10000.0;
                            asnOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                                    .setWorkQty(workQty.compareTo(0.00) > 0 ? workQty : 0.00 )
                                    .setUpdateBy(loginUserId)
                                    .setUpdateTime(new Date());
                            if (!outStockService.updateById(asnOrder)) {
                                throw new CoolException("原单据状态修改失败!!");
                            }
                            loc.setUseStatus(LocStsType.LOC_STS_TYPE_F.type).setBarcode(task.getBarcode()).setUpdateBy(loginUserId).setUpdateTime(new Date());
                            if (!locService.updateById(loc)) {
                                throw new CoolException("库位信息修改失败!!");
                            AsnOrderItem orderItem = outStockItemService.getById(item.getSource());
                            if (Objects.isNull(orderItem)) {
                                throw new CoolException("数据错误:单据明细已不存在!!");
                            }
                            Double workItmQty = Math.round((orderItem.getWorkQty() - item.getAnfme()) * 10000) / 10000.0;
                            orderItem.setWorkQty(workItmQty);
                            if (!outStockItemService.updateById(orderItem)) {
                                throw new CoolException("原单据明细修改失败!!");
                            }
                        }
                    } else {
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -125,9 +125,9 @@
            /***将有货有的明细信息存放到库位信息中*/
            for (int i = 0; i < items.size(); i++) {
                items.get(i)
                        .setWaveId(param.getWaveId())
                        .setWaveCode(param.getWaveCode())
                        .setWaveItemId(param.getId());
                        .setSourceId(param.getWaveId())
                        .setSourceCode(param.getWaveCode())
                        .setSource(param.getId());
            }
            locItemList.addAll(items);
        }
@@ -141,7 +141,7 @@
        listMap.keySet().forEach(key -> {
            List<LocItem> locItems = listMap.get(key);
            LocItem item1 = locItems.stream().findFirst().get();
            WaveItem waveItem = waveItemService.getById(item1.getWaveItemId());
            WaveItem waveItem = waveItemService.getById(item1.getSource());
            if (null == waveItem || Objects.isNull(waveItem)) {
                throw new CoolException("数据错误:波次明细不存在!!");
            }
@@ -183,7 +183,7 @@
                        .setId(null)
                        .setSourceCode(wave.getCode())
                        .setSourceId(wave.getId())
                        .setSource(item.getWaveItemId());
                        .setSource(item.getSource());
                taskItems.add(taskItem);
            }
            if (!taskItemService.saveBatch(taskItems)) {
rsf-server/src/main/resources/application.yml
@@ -25,7 +25,7 @@
  #  global-config:
  #    field-strategy: 0
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
    cache-enabled: true
    call-setters-on-nulls: true