24个文件已修改
10个文件已添加
1个文件已删除
1 文件已重命名
1940 ■■■■ 已修改文件
rsf-admin/src/i18n/en.js 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/ResourceContent.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/purchase/PurchaseList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/outWork/outBound/OutBoundList.jsx 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/outWork/outBound/StaSelect.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/outWork/outBound/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/task/TaskList.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waitPakin/WaitPakinList.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/basContainer.sql 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/params/TaskInParam.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/AgvServiceImpl.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/utils/LocUtils.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/domain/PageParam.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/utils/FieldsUtils.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/BasStationController.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemWorkingController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/TaskController.java 113 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/BasStation.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItemWorking.java 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Task.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/LocType.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemWorkingMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemWorkingService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemWorkingServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 289 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaitPakinServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/LocItemWorkingMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -207,8 +207,12 @@
    },
    table: {
        field: {
            outBound: {
                stockWithdrawal: 'StockWithdrawal',
                withdrawal: 'Withdrawal'
            },
            basContainer: {
                containerType:'containerType',
                containerType: 'containerType',
                codeType: 'codeType',
                areas: 'areas',
            },
@@ -224,8 +228,8 @@
                isWcs: 'isWcs',
                wcsData: 'wcsData',
                containerType: 'containerType',
                barcode: 'barcode',
                autoTransfer: 'autoTransfer',
                barcode: 'barcode',
                autoTransfer: 'autoTransfer',
            },
            host: {
@@ -1113,6 +1117,7 @@
        locInit: 'loc init',
        siteInit: 'site init',
        batch: 'batch',
        pick: 'Pick',
        confirm: 'confirm',
        cancel: "cancel",
        query: "Query",
rsf-admin/src/i18n/zh.js
@@ -1,4 +1,5 @@
import basContainer from "../page/basicInfo/basContainer";
import outBound from "../page/outWork/outBound";
import chineseMessages from "./core/chineseMessages";
const customChineseMessages = {
@@ -187,7 +188,7 @@
        taskItem: '任务档明细',
        taskLog: '任务历史档',
        taskItemLog: '任务明细历史档',
        stock: '入出库存信息',
        stock: '入出库历史',
        stockItem: '单据明细',
        locItem: '库位明细',
        histories: '历史档',
@@ -207,9 +208,19 @@
        wave: '波次管理',
        basStation: '站点信息',
        basContainer: '容器管理',
        outBound: '出库作业',
    },
    table: {        
        field: {
            outBound: {
                stockWithdrawal: '提取库存',
                withdrawal:'提取',
                outSta: '出库站',
                outQty: '出库数量',
                anfme: '数量',
                createTask: '生成任务',
            },
            basContainer: {
                containerType:'容器类型',
                codeType: '条码类型',
@@ -1105,6 +1116,7 @@
        continue: '继续收货',
        batch: '批量操作',
        confirm: '确认',
        pick: '拣料',
        bulkExport: "批量导出",
        selectSite: '选择站点',
        confirmSelect: '确认选择',
rsf-admin/src/page/ResourceContent.js
@@ -49,6 +49,7 @@
import basStation from './basicInfo/basStation';
import warehouseStock from './statistics/stockManage';
import basContainer from './basicInfo/basContainer';
import outBound from "./outWork/outBound";
const ResourceContent = (node) => {
    switch (node.component) {
@@ -142,6 +143,8 @@
            return basStation;
        case 'basContainer':
            return basContainer;
        case 'outBound':
            return outBound;
        default:
            return {
                list: ListGuesser,
rsf-admin/src/page/orders/purchase/PurchaseList.jsx
@@ -124,7 +124,7 @@
        <StyledDatagrid
          preferenceKey='purchase'
          bulkActionButtons={() => <BulkDeleteButton mutationMode="pessimistic"/>}
          rowClick={'edit'}
          rowClick={false}
          expand={false}
          expandSingle={true}
          omit={['id', 'createTime', 'createBy','channel', 'platCode', 'memo', 'channel','startTime','workQty', 'endTime']}
@@ -151,7 +151,7 @@
          {/* <BooleanField source="statusBool" label="common.field.status" sortable={false} /> */}
          <TextField source="memo" label="common.field.memo" sortable={false} />
          <WrapperField cellClassName="opt" label="common.field.opt">
            <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
            <EditButton label="toolbar.detail" sx={{ padding: '1px', fontSize: '.75rem' }} />
            <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode="pessimistic"/>
          </WrapperField>
        </StyledDatagrid>
rsf-admin/src/page/outWork/outBound/OutBoundList.jsx
New file
@@ -0,0 +1,329 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { useWatch, useFormContext } from "react-hook-form";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
    useListContext,
    useRefresh,
    Edit,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    TableContainer,
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled,
    Select,
    MenuItem,
    Typography,
    Card,
} from '@mui/material';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import ConfirmButton from "../../components/ConfirmButton";
import TreeSelectInput from "@/page/components/TreeSelectInput";
import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
import DictSelect from "../../components/DictSelect";
import AddIcon from '@mui/icons-material/Add';
import DeleteIcon from '@mui/icons-material/Delete';
import request from '@/utils/request';
import LocItemInfoModal from "./locItemInfoModal";
import { Delete } from '@mui/icons-material';
import _, { set } from 'lodash';
import StaSelect from "./StaSelect";
const OutBoundList = () => {
    const [createDialog, setCreateDialog] = useState(false);
    const [tabelData, setTableData] = useState([]);
    const [selectedRows, setSelectedRows] = useState([]);
    const [sta,setSta] = useState("");
    const notify = useNotify();
    const tableRef = useRef();
    tableRef.current = useGridApiRef();
    const translate = useTranslate();
    const handleDeleteItem = () => {
        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
        setTableData(newTableData);
    }
    // 添加一个处理新数据的函数,设置outQty默认值
    const handleSetData = (newData) => {
        // 为新添加的数据设置outQty默认值为anfme的值
        const dataWithDefaultQty = newData.map(item => ({
            ...item,
            outQty: item.outQty || item.anfme // 如果outQty已存在则保留,否则使用anfme的值
        }));
        setTableData([...tabelData, ...dataWithDefaultQty]);
    };
    return (
        <>
        <Card sx={{ p: 2, mb: 2, mt:2}}>
            <Grid container spacing={2}>
                <Grid item xs={12}>
                    <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
                        <Typography variant="h6">
                            {translate('table.field.outBound.stockWithdrawal')}
                        </Typography>
                        <Stack direction='row' spacing={2}>
                            <Button
                                variant="contained"
                                color="primary"
                                startIcon={<AddIcon />}
                                onClick={() => setCreateDialog(true)}
                            >
                                {translate('table.field.outBound.withdrawal')}
                            </Button>
                        </Stack>
                    </Box>
                </Grid>
            </Grid>
        </Card>
        <Card sx={{ p: 2, mb: 2}}>
            <Form>
            <Grid container spacing={2}>
                <Grid item xs={12}>
                    <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
                        <Typography  variant="h6" >
                            {translate('table.field.outBound.outSta')}
                         </Typography>
                        <Stack direction='row' spacing={2} minWidth={200}>
                            <StaSelect
                                source="sta"
                                label={translate("table.field.outBound.outSta")}
                                onChange={(e) => {
                                    setSta(e.target.value);
                                    console.log("站点已选择:", e.target.value);
                                }}
                                size="small"
                                type="1"
                            />
                        </Stack>
                        <Stack direction='row' spacing={2} minWidth={200}>
                            <SubmitButton
                                sta={sta}
                                data={tabelData}
                            />
                        </Stack>
                    </Box>
                </Grid>
            </Grid>
            </Form>
        </Card>
        <Card sx={{ mb: 2}}>
            <Box sx={{  }}>
                <ModalTable tabelData={tabelData} setTableData={setTableData}  selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></ModalTable>
            </Box>
        </Card>
        <LocItemInfoModal
                open={createDialog}
                setOpen={setCreateDialog}
                data={tabelData}
                setData={handleSetData}
            />
        </>
    )
}
export default OutBoundList;
const SubmitButton = (props) =>{
    const translate = useTranslate();
    const notify = useNotify();
    const { sta, data } = props;
    const check = ()=>{
        if(sta === "" || sta === undefined || sta === null){
            notify("请选择站点");
            return;
        }
        if(data.length === 0){
            notify("请选择物料");
            return;
        }
        http(sta,data);
    }
    const http = async (sta,data) => {
        console.log("提交数据",sta,data);
    }
    return (
        <Button
            variant="contained"
            color="primary"
            onClick={check}
        >
          {translate('table.field.outBound.createTask')}
        </Button>
    )
}
const ModalTable = ({ tabelData, setTableData, selectedRows, setSelectedRows, tableRef }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [columns, setColumns] = useState([
        {
            field: 'outQty',
            headerName: translate('table.field.outBound.outQty')+"*",
            width: 100,
            editable: true,
            headerClassName: "custom",
        },
        {
            field: 'anfme',
            headerName: translate('table.field.locItem.anfme'),
            width: 100,
            editable: false,
        },
        {
            field: 'matnrCode',
            headerName: translate('table.field.locItem.matnrCode'),
            width: 130,
            editable: false,
        },
        {
            field: 'maktx',
            headerName: translate('table.field.locItem.maktx'),
            width: 250,
            editable: false,
        },
        {
            field: 'batch',
            headerName: translate('table.field.locItem.batch'),
            width: 250,
            editable: false,
        },
    ])
    const action = {
        field: 'action',
        headerName: '操作',
        width: 70,
        lockPosition: 'left',
        renderCell: (params) => (
            <Tooltip title="Delete">
                <IconButton onClick={() => handleDelete(params.row)}>
                    <Delete />
                </IconButton>
            </Tooltip>
        ),
    }
    let cdata = useRef([]);
    useEffect(() => {
        getDynamicFields();
    }, []);
    useEffect(() => {
        cdata.current = tabelData
    }, [tabelData]);
    const getDynamicFields = async () => {
        const {
            data: { code, data, msg },
        } = await request.get("/fields/enable/list");
        if (code === 200) {
            const cols = data.map(el => ({
                field: el.fields,
                headerName: el.fieldsAlise,
                minWidth: 100,
                flex: 1,
                editable: false
            }))
            setColumns([...columns, ...cols, action])
        } else {
            notify(msg);
        }
    }
    const handleDelete = (row) => {
        const newData = _.filter(cdata.current, (item) => item.id !== row.id);
        setTableData(newData);
    };
    const processRowUpdate = (newRow, oldRow) => {
        const rows = tabelData.map((r) =>
            r.id === newRow.id ? { ...newRow } : r
        )
        setTableData(rows)
        return newRow;
    };
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    tableRef.current = useGridApiRef();
    return (
        <div style={{ height: 500, width: '100%' }}>
            <DataGrid
                apiRef={tableRef}
                rows={tabelData}
                columns={columns}
                disableRowSelectionOnClick
                getRowId={(row) => row.matnrId ? row.matnrId : row.id}
                disableColumnFilter
                disableColumnSelector
                disableColumnSorting
                disableMultipleColumnsSorting
                processRowUpdate={processRowUpdate}
                initialState={{
                    pagination: {
                        paginationModel: {
                            pageSize: 25,
                        },
                    },
                }}
                pageSizeOptions={[10, 25, 50, 100]}
                editMode="row"
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                sx={{
                    '& .MuiDataGrid-cell input': {
                        border: '1px solid #ccc'
                    },
                }}
            />
        </div>
    );
};
rsf-admin/src/page/outWork/outBound/StaSelect.jsx
New file
@@ -0,0 +1,70 @@
import EditIcon from '@mui/icons-material/Edit';
import { useState, useEffect } from 'react';
import {
    Button, useListContext, SelectInput,
    required, SelectArrayInput,
    useTranslate, useNotify,
    SelectArrayInputClasses
} from 'react-admin';
import request from '@/utils/request';
const StaSelect = (props) => {
    const {
        type ,
        name,
        multiple = false,
        perPage = 100,  // 默认每页显示100条数据
        page = 1,       // 默认第一页
        ...parmas
    } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const [list, setList] = useState([]);
    const [loading, setLoading] = useState(false);
    useEffect(() => {
        http();
    }, [type, page, perPage]);
    const http = async () => {
        setLoading(true);
        try {
            const res = await request.post('/deviceSite/page', {
                type: type,
                current: page,
                pageSize: perPage
            });
            if (res?.data?.code === 200) {
                setList(res.data.data.records.map((item) => {
                    return {
                        id: item.site,
                        name: item.site
                    }
                }));
            } else {
                notify(res.data.msg);
            }
        } catch (error) {
            notify('加载站点失败', 'error');
            console.error('加载站点失败:', error);
        } finally {
            setLoading(false);
        }
    };
    const InputComponent = multiple ? SelectArrayInput : SelectInput;
    return (
        <SelectInput
            source={name}
            choices={list}
            isLoading={loading}
            optionValue='id'
            optionText='name'
            {...parmas}
        />
    );
};
export default StaSelect;
rsf-admin/src/page/outWork/outBound/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import OutBoundList from "./OutBoundList";
export default {
    list: OutBoundList,
    edit: EditGuesser,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx
New file
@@ -0,0 +1,227 @@
import React, { useState, useEffect } from "react";
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    styled
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import { useTranslate, useNotify, useRefresh } from 'react-admin';
import request from '@/utils/request';
import { DataGrid } from '@mui/x-data-grid';
import SaveIcon from '@mui/icons-material/Save';
import TreeSelectInput from "@/page/components/TreeSelectInput";
const LocItemInfoModal = (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);
        }
    };
    const [formData, setFormData] = useState({});
    const [tableData, setTableData] = useState([]);
    const [dyFields, setDyFields] = useState([]);
    const [selectedRows, setSelectedRows] = useState([]);
    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(() => ({
            [name]: value
        }));
    };
    const reset = () => {
        setFormData({
        })
    }
    const handleSubmit = () => {
        const hasarr = data.map(el => +el.id)
        const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id)));
        const value = selectedData.map((el => {
            const dynamicFields = dyFields.reduce((acc, item) => {
                acc[item.fields] = el['extendFields']?.[item.fields] || '';
                return acc;
            }, {});
            return {
                ...el,
                outQty: el.anfme, // 设置outQty默认值为anfme的值
                ...dynamicFields
            }
        }))
        setData([...value]);
        setOpen(false);
        reset();
    };
    const getData = async () => {
        const res = await request.post(`/locItem/useO/page`, {
            ...formData,
            current: 1,
            pageSize: 100,
            orderBy: "create_time desc"
        });
        if (res?.data?.code === 200) {
            setTableData(res.data.data.records);
        } else {
            notify(res.data.msg);
        }
    };
    useEffect(() => {
        getData();
    }, [open]);
    const handleSearch = () => {
        getData()
    };
    return (
        <Dialog
            open={open}
            onClose={handleClose}
            aria-labelledby="form-dialog-title"
            fullWidth
            disableRestoreFocus
            maxWidth="lg"
        >
            <DialogTitle id="form-dialog-title" sx={{
                position: 'sticky',
                top: 0,
                backgroundColor: 'background.paper',
                zIndex: 1000
            }}>
                {translate("common.action.newAddMats")}
                <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                    <DialogCloseButton onClose={handleClose} />
                </Box>
            </DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
                <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                    <Grid container spacing={2}>
                        <Grid item md={2}>
                            <TextField
                                label={translate('table.field.locItem.maktx')}
                                name="maktx"
                                value={formData.maktx}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                        <Grid item md={2}>
                            <TextField
                                label={translate('table.field.locItem.matnrCode')}
                                name="matnrCode"
                                value={formData.matnrCode}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                    </Grid>
                </Box>
                <Box sx={{ mt: 2 }}>
                    <Stack direction="row" spacing={2}>
                        <Button variant="contained" onClick={handleSearch}>搜索</Button>
                    </Stack>
                </Box>
                <Box sx={{ mt: 2, height: 400, width: '100%' }}>
                    <AsnWareModalTable
                        tableData={tableData}
                        setTableData={setTableData}
                        dyFields={dyFields}
                        setDyFields={setDyFields}
                        selectedRows={selectedRows}
                        setSelectedRows={setSelectedRows}
                    />
                </Box>
            </DialogContent>
            <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
                    <Button onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
                        {translate('toolbar.confirm')}
                    </Button>
                </Box>
            </DialogActions>
        </Dialog>
    );
};
export default LocItemInfoModal;
const AsnWareModalTable = ({ tableData, setTableData, selectedRows, setSelectedRows, dyFields, setDyFields }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [columns, setColumns] = useState([
        // { field: 'id', headerName: 'ID', width: 100 },
        { field: 'locCode', headerName: translate('table.field.locItem.locCode'), width: 100 },
        { field: 'matnrCode', headerName: translate('table.field.locItem.matnrCode'), width: 200 },
        { field: 'maktx', headerName: translate('table.field.locItem.maktx'), width: 300 },
        { field: 'batch', headerName: translate('table.field.locItem.batch'), width: 100 },
        { field: 'anfme', headerName: translate('table.field.locItem.anfme'), width: 100 },
        { field: 'unit', headerName: translate('table.field.locItem.unit'), width: 100 },
    ])
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    useEffect(() => {
        getDynamicFields();
    }, []);
    const getDynamicFields = async () => {
        const {
            data: { code, data, msg },
        } = await request.get("/fields/enable/list");
        if (code === 200) {
            const cols = data.map(el => ({
                field: el.fields,
                headerName: el.fieldsAlise,
                minWidth: 100,
                flex: 1,
                editable: el.unique,
                valueGetter: (value, row) => {
                    return row.extendFields?.[el.fields] || '';
                },
            }))
            setDyFields(data)
            setColumns([...columns, ...cols])
        } else {
            notify(msg);
        }
    }
    return (
        <div style={{ height: 400, width: '100%' }}>
            <DataGrid
                size="small"
                rows={tableData}
                columns={columns}
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                disableColumnMenu={true}
                disableColumnSorting
                disableMultipleColumnsSorting
            />
        </div>
    );
};
rsf-admin/src/page/task/TaskList.jsx
@@ -25,6 +25,7 @@
    SelectInput,
    NumberInput,
    Button,
    EditButton,
} from 'react-admin';
import { Box, Typography, Card, Stack, Drawer } from '@mui/material';
import { styled } from '@mui/material/styles';
@@ -42,6 +43,7 @@
import ConfirmButton from "../components/ConfirmButton";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import ColorizeOutlinedIcon from '@mui/icons-material/ColorizeOutlined';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -129,7 +131,7 @@
                            <BulkDeleteButton mutationMode={OPERATE_MODE} />
                        </>
                    }
                    rowClick={'edit'}
                    rowClick={false}
                    expand={false}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo', 'robotCode', 'exceStatus', 'expDesc', 'expCode', 'status', 'sort']}
@@ -156,9 +158,10 @@
                    {/* <BooleanField source="statusBool" label="common.field.status" sortable={false} /> */}
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt" onClick={(e) => e.stopPropagation()} >
                        <EditButton label="toolbar.detail" />
                        <DoneButton sx={{ padding: '1px', fontSize: '.75rem' }} ></DoneButton>
                        <CancelButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                        <SetTopButton sx={{ padding: '1px', fontSize: '.75rem' }} ></SetTopButton>
                        <PickButton />
                    </WrapperField>
                </StyledDatagrid>
            </List>
@@ -176,6 +179,25 @@
/**
 * 拣料出库
 * @returns
 */
const PickButton = () => {
    const record = useRecordContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const pickClick = () => {
    }
    return (
        record?.taskStatus == 103 ? <ConfirmButton label={"toolbar.pick"} startIcon={<ColorizeOutlinedIcon />} onConfirm={pickClick}/> : <></>
    )
}
/**
 * 完成操作
 * @returns 
 */
rsf-admin/src/page/waitPakin/WaitPakinList.jsx
@@ -93,7 +93,7 @@
    const [drawerVal, setDrawerVal] = useState(false);
    const CreateTaskButton = () => {
        const record = useRecordContext();
        const record = useRecordContext();
        const notify = useNotify();
        const refresh = useRefresh();
        const { selectedIds, onUnselectItems, data } = useListContext();
@@ -115,7 +115,7 @@
    }
    const CreateTaskRowButton = () => {
        const record = useRecordContext();
        const record = useRecordContext();
        const refresh = useRefresh();
        const createTask = (event) => {
@@ -124,7 +124,7 @@
            setSource([record])
            refresh()
        }
        return (
            <>
                <Button onClick={(event) => createTask(event)} label={"toolbar.createTask"}>
@@ -166,7 +166,7 @@
                            <CreateTaskButton />
                            {/* <BulkDeleteButton mutationMode={OPERATE_MODE} /> */}
                        </>}
                    rowClick='edit'
                    rowClick={false}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
rsf-server/src/main/java/basContainer.sql
File was deleted
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java
@@ -63,8 +63,26 @@
        return R.ok();
    }
    /**
     * 接收WCS调度,回传执行状态
     *  //TODO 1. 拣料出库,再入库执行,修改状态为199.任务完成  并记录入库站点(源站点),添加任务号参数
     *
     * @param param
     * @return
     */
    @ApiOperation("接收WCS调度,回传执行状态")
    @PostMapping("/exce/status")
    public R receiveTask(@RequestBody TaskInParam param) {
        if (Cools.isEmpty(param)) {
            return R.error("参数不能为空!!");
        }
        return R.ok();
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/params/TaskInParam.java
@@ -1,14 +1,33 @@
package com.vincent.rsf.server.api.controller.params;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@ApiModel(value = "TaskInParam", description = "WCS调度参数")
public class TaskInParam {
    @ApiModelProperty("作业类型")
    private Integer ioType;  //作业类型
    @ApiModelProperty("作业站点 or 来源站点")
    private Integer sourceStaNo; //作业站点 or 来源站点
    @ApiModelProperty("容器条码")
    private String barcode; //容器条码
    @ApiModelProperty("库位类型")
    private Integer locType1; //库位类型
    @ApiModelProperty("任务编码")
    private String taskCode;
//    @ApiModelProperty("库位类型")
//    private String locType;
    private Long user;
//    private Integer locType2; //库位类型
//    private Integer locType3; //库位类型
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/AgvServiceImpl.java
@@ -1,14 +1,14 @@
package com.vincent.rsf.server.api.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.service.AgvService;
import com.vincent.rsf.server.manager.entity.BasStation;
import com.vincent.rsf.server.manager.entity.WaitPakin;
import com.vincent.rsf.server.manager.entity.WaitPakinItem;
import com.vincent.rsf.server.manager.entity.WarehouseAreas;
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.enums.PakinIOStatus;
import com.vincent.rsf.server.manager.enums.StaUseStatusType;
import com.vincent.rsf.server.manager.service.*;
@@ -18,6 +18,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -34,6 +35,8 @@
    private WarehouseAreasService warehouseAreasService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private BasContainerService basContainerService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -256,6 +259,20 @@
        if (!basStation.getUseStatus().equals("O")){
            throw new CoolException("站点状态不为空闲");
        }
        if (!Cools.isEmpty(basStation.getContainerType())){
            List<Long> longs1 = JSONObject.parseArray(basStation.getContainerType(), Long.class);
            List<BasContainer> containers = basContainerService.list(
                    new LambdaQueryWrapper<BasContainer>()
                            .in(BasContainer::getContainerType, longs1)
            );
            boolean matches = containers.stream()
                    .map(BasContainer::getCodeType)
                    .anyMatch(codeType -> barcode.matches(codeType));
            if (!matches) {
                throw new CoolException("条码与站点不匹配");
            }
        }
        return basStation;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java
@@ -118,7 +118,6 @@
    }
    /**
     * 验证设备站点
     */
@@ -298,10 +297,11 @@
//            waitPakinItems.forEach(item -> {
//                TaskItem taskItem = new TaskItem();
//                BeanUtils.copyProperties(item, taskItem);
////                AsnOrder order = asnOrderService.getOne(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getId, item.getAsnId()));
////                if (Objects.isNull(order)) {
////                    throw new CoolException("数据错误: 单据不存在!!");
////                }
    /// /                AsnOrder order = asnOrderService.getOne(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getId, item.getAsnId()));
    /// /                if (Objects.isNull(order)) {
    /// /                    throw new CoolException("数据错误: 单据不存在!!");
    /// /                }
//                taskItem.setTaskId(task.getId())
//                        .setOrderType(OrderType.ORDER_RECEIPT.type)
//                        .setSource(item.getId())
@@ -327,19 +327,19 @@
//        locNo.setWorkNo(ruleCode);
//        return locNo;
//    }
    public InTaskMsgDto getLocNo(TaskInParam param) {
        String matnr = null; String batch = null;
        String matnr = null;
        String batch = null;
        List<WaitPakin> waitPakins = waitPakinService.list(new LambdaQueryWrapper<WaitPakin>().eq(WaitPakin::getBarcode, param.getBarcode()));
        if (Cools.isEmpty(waitPakins) && param.getIoType().equals(TaskType.TASK_TYPE_IN.type)) {
            throw new CoolException("未找到组托信息,请组托");
        }else if (!Cools.isEmpty(waitPakins)) {
        } else if (!Cools.isEmpty(waitPakins)) {
            matnr = waitPakins.get(0).getCode();
            batch = waitPakins.get(0).getCode();
        }
        List<DeviceSite> deviceSites = deviceSiteService.list(new LambdaQueryWrapper<DeviceSite>()
                .eq(DeviceSite::getSite, param.getSourceStaNo())
                .eq(DeviceSite::getType,param.getIoType())
                .eq(DeviceSite::getType, param.getIoType())
        );
        if (Cools.isEmpty(deviceSites)) {
            throw new CoolException("未找到站点路径信息");
@@ -356,18 +356,18 @@
        InTaskMsgDto dto = null;
        switch (warehouseArea.getType()) {
            case "CRN": //堆垛机
                dto = getLocNoCrn(deviceBind,warehouseArea.getId(), param.getSourceStaNo(), matnr,batch, locTypeDto, 0, param.getIoType());
                dto = getLocNoCrn(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
                break;
            case "SXC": //四向库
                break;
            case "CTU": //ctu
                dto = getLocNoCtu(deviceBind,warehouseArea.getId(), param.getSourceStaNo(), matnr,batch, locTypeDto, 0, param.getIoType());
                dto = getLocNoCtu(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
                break;
        }
        return dto;
    }
    private InTaskMsgDto getLocNoCrn(DeviceBind deviceBind,Long area,Integer sourceStaNo, String matnr, String batch,LocTypeDto locTypeDto, int times,Integer ioType){
    private InTaskMsgDto getLocNoCrn(DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
        if (Cools.isEmpty(matnr)) {  //物料号
            matnr = "";
        }
@@ -404,7 +404,7 @@
            throw new CoolException("无可用堆垛机");
        }
        //入库靠近摆放
        if (ioType== 1 && deviceBind.getBeSimilar().equals("1") && !Cools.isEmpty(matnr)) {
        if (ioType == 1 && deviceBind.getBeSimilar().equals("1") && !Cools.isEmpty(matnr)) {
            if (nearRow != curRow) {
                List<LocItem> locItems = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getMatnrCode, matnr));
                for (LocItem locItem : locItems) {
@@ -414,12 +414,12 @@
                    }
                    String shallowLocNo = LocUtils.getShallowLoc(slaveProperties, loc1.getCode());
                    // 检测目标库位是否为空库位
                    Loc shallowLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode,shallowLocNo));
                    Loc shallowLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, shallowLocNo));
                    if (shallowLoc != null && shallowLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) {
                        if (LocUtils.locMoveCheckLocTypeComplete(shallowLoc, locTypeDto)) {
                                loc = shallowLoc;
                                deviceNo = shallowLoc.getDeviceNo();
                                break;
                            loc = shallowLoc;
                            deviceNo = shallowLoc.getDeviceNo();
                            break;
                        }
                    }
@@ -457,9 +457,9 @@
                .eq(DeviceSite::getSite, sourceStaNo)
                .eq(DeviceSite::getDeviceCode, deviceNo)
        );
        if (Cools.isEmpty(deviceSite)){
        if (Cools.isEmpty(deviceSite)) {
            deviceNo = 0;
        }else {
        } else {
            inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite()));
        }
@@ -476,7 +476,7 @@
                    .eq(Loc::getRow, nearRow)
                    .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                    .eq(Loc::getType, locTypeDto.getLocType1())
                    .eq(Loc::getAreaId,area)
                    .eq(Loc::getAreaId, area)
                    .orderByAsc(Loc::getLev)
                    .orderByAsc(Loc::getCol)
            );
@@ -485,12 +485,12 @@
                    continue;
                }
                String shallowLoc = LocUtils.getDeepLoc(slaveProperties, locMast1.getCode());
                if ((ioType== 1 && deviceBind.getBeSimilar().equals("1"))) {
                if ((ioType == 1 && deviceBind.getBeSimilar().equals("1"))) {
                    //相似物料打开,判断深库位有没有货,没货就放深库位,有货就不操作
                    Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                            .eq(Loc::getRow, shallowLoc)
                            .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                            .eq(Loc::getAreaId,area)
                            .eq(Loc::getAreaId, area)
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        loc = locMast2;
@@ -500,17 +500,17 @@
                    //相似物料关闭,判断深库位有没有货,有货就放浅库位,无货就不操作
                    Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                            .eq(Loc::getCode, shallowLoc)
                            .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type,LocStsType.LOC_STS_TYPE_F.type)
                            .eq(Loc::getAreaId,area)
                            .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type, LocStsType.LOC_STS_TYPE_F.type)
                            .eq(Loc::getAreaId, area)
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        loc = locMast1;
                        break;
                    }else{
                    } else {
                        locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                                .eq(Loc::getCode, shallowLoc)
                                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                                .eq(Loc::getAreaId,area)
                                .eq(Loc::getAreaId, area)
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            loc = locMast2;
@@ -529,7 +529,7 @@
                        Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                                .eq(Loc::getCode, shallowLoc)
                                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                                .eq(Loc::getAreaId,area)
                                .eq(Loc::getAreaId, area)
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            loc = locMast2;
@@ -537,8 +537,8 @@
                        } else {
                            locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                                    .eq(Loc::getCode, shallowLoc)
                                    .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type,LocStsType.LOC_STS_TYPE_D.type)
                                    .eq(Loc::getAreaId,area)
                                    .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type, LocStsType.LOC_STS_TYPE_D.type)
                                    .eq(Loc::getAreaId, area)
                            );
                            if (!Cools.isEmpty(locMast2)) {
                                loc = locMast1;
@@ -556,12 +556,12 @@
        }
        //查询当前库位类型空库位 小于5个则locmast = null
        List<Loc> locTypeLocMasts = locService.list(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getUseStatus,LocStsType.LOC_STS_TYPE_O.type)
                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                .eq(Loc::getDeviceNo, deviceNo)
                .eq(Loc::getType, locTypeDto.getLocType1())
                .eq(Loc::getAreaId,area)
                .eq(Loc::getAreaId, area)
        );
        if (null !=locTypeLocMasts && locTypeLocMasts.size()<=5){
        if (null != locTypeLocMasts && locTypeLocMasts.size() <= 5) {
            loc = null;
        }
        // 递归查询
@@ -569,14 +569,14 @@
            // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归
            if (times < rowCount * 2) {
                times = times + 1;
                return getLocNoCrn(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,times, ioType);
                return getLocNoCrn(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
            }
            // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位
            if (locTypeDto.getLocType1() < 3) {
                int i = locTypeDto.getLocType1() + 1;
                locTypeDto.setLocType1(i);
                return getLocNoCrn(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,0, ioType);
                return getLocNoCrn(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
            }
            throw new CoolException("没有空库位");
        }
@@ -590,7 +590,7 @@
        return inTaskMsgDto;
    }
    private InTaskMsgDto getLocNoCtu(DeviceBind deviceBind,Long area,Integer sourceStaNo, String matnr, String batch,LocTypeDto locTypeDto, int times,Integer ioType){
    private InTaskMsgDto getLocNoCtu(DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
        if (Cools.isEmpty(matnr)) {  //物料号
            matnr = "";
        }
@@ -608,7 +608,7 @@
                .orderByAsc(Loc::getCol)
                .orderByAsc(Loc::getRow)
        );
        for (Loc loc2 :loc1){
        for (Loc loc2 : loc1) {
            if (!LocUtils.locMoveCheckLocTypeComplete(loc2, locTypeDto)) {
                continue;
            }
@@ -621,10 +621,10 @@
                .eq(DeviceSite::getSite, sourceStaNo)
                .eq(DeviceSite::getDeviceCode, loc.getDeviceNo())
        );
        if (Cools.isEmpty(deviceSite)){
        if (Cools.isEmpty(deviceSite)) {
            deviceNo = 0;
            loc = null;
        }else {
        } else {
            inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite()));
        }
        // 递归查询
@@ -632,14 +632,14 @@
            // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归
            if (times < 5) {
                times = times + 1;
                return getLocNoCtu(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,times, ioType);
                return getLocNoCtu(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
            }
            // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位
            if (locTypeDto.getLocType1() < 3) {
                int i = locTypeDto.getLocType1() + 1;
                locTypeDto.setLocType1(i);
                return getLocNoCtu(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,0, ioType);
                return getLocNoCtu(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
            }
            throw new CoolException("没有空库位");
        }
rsf-server/src/main/java/com/vincent/rsf/server/api/utils/LocUtils.java
@@ -150,6 +150,7 @@
        // 如果源库位是高库位,目标库位是低库位
        return dto.getLocType1().equals(Integer.parseInt(loc.getType()));
    }
    public static String zerofill(String msg, Integer count) {
        if (msg.length() == count) {
            return msg;
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
@@ -22,7 +22,7 @@
//        generator.username="sa";
//        generator.password="Zoneyung@zy56$";
        generator.table = "man_bas_container";
        generator.table = "man_loc_item_working";
        generator.tableDesc = "容器管理";
        generator.packagePath = "com.vincent.rsf.server.manager";
rsf-server/src/main/java/com/vincent/rsf/server/common/domain/PageParam.java
@@ -82,7 +82,9 @@
        Map<String, Object> map = where.getMap();
        for (String key : map.keySet()) {
            Object val = map.get(key);
            if (Cools.isEmpty(val)){
                continue;
            }
            if (key.contains("Range")) {
                ArrayList<String> list = null;
                if (val instanceof ArrayList) {
@@ -175,7 +177,9 @@
        Map<String, Object> map = where.getMap();
        for (String key : map.keySet()) {
            Object val = map.get(key);
            if (Cools.isEmpty(val)){
                continue;
            }
            if (key.contains("Range")) {
                ArrayList<String> list = null;
                if (val instanceof ArrayList) {
rsf-server/src/main/java/com/vincent/rsf/server/common/utils/FieldsUtils.java
@@ -13,6 +13,7 @@
import com.vincent.rsf.server.system.entity.FieldsItem;
import com.vincent.rsf.server.system.service.FieldsItemService;
import com.vincent.rsf.server.system.service.FieldsService;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
@@ -145,8 +146,9 @@
     * @return
     * @time 2025/4/7 15:28
     */
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    public static synchronized  void updateFieldsValue(Map<String, Object> params) throws Exception {
    public static void updateFieldsValue(Map<String, Object> params) throws Exception {
        List<Fields> fields = getFieldsSta();
        if (fields.isEmpty()) { return; }
        Object fieldsIndex = params.get("fieldsIndex");
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/BasStationController.java
@@ -1,5 +1,7 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
@@ -34,22 +36,12 @@
        PageParam<BasStation, BaseParam> page = basStationService.page(pageParam, pageParam.buildWrapper(true));
        for (BasStation station : page.getRecords()) {
            if (!Cools.isEmpty(station.getCrossZoneArea())) {
                String content = station.getCrossZoneArea().substring(1, station.getCrossZoneArea().length() - 1);
                String[] parts = content.split(",");
                Long[] longArray = new Long[parts.length];
                for (int i = 0; i < parts.length; i++) {
                    longArray[i] = Long.parseLong(parts[i].trim());
                }
                station.setAreaIds(longArray);
                List<Long> longs1 = JSONObject.parseArray(station.getCrossZoneArea(), Long.class);
                station.setAreaIds(longs1);
            }
            if (!Cools.isEmpty(station.getContainerType())) {
                String content = station.getContainerType().substring(1, station.getContainerType().length() - 1);
                String[] parts = content.split(",");
                Long[] longArray = new Long[parts.length];
                for (int i = 0; i < parts.length; i++) {
                    longArray[i] = Long.parseLong(parts[i].trim());
                }
                station.setContainerTypes(longArray);
                List<Long> longs1 = JSONObject.parseArray(station.getContainerType(), Long.class);
                station.setContainerTypes(longs1);
            }
        }
@@ -72,26 +64,16 @@
    @GetMapping("/basStation/{id}")
    public R get(@PathVariable("id") Long id) {
        BasStation station = basStationService.getById(id);
        if (!Cools.isEmpty(station.getCrossZoneArea())) {
            String content = station.getCrossZoneArea().substring(1, station.getCrossZoneArea().length() - 1);
            String[] parts = content.split(",");
            Long[] longArray = new Long[parts.length];
            for (int i = 0; i < parts.length; i++) {
                longArray[i] = Long.parseLong(parts[i].trim());
            }
            station.setAreaIds(longArray);
            List<Long> longs1 = JSONObject.parseArray(station.getCrossZoneArea(), Long.class);
            station.setAreaIds(longs1);
        }
        if (!Cools.isEmpty(station.getContainerType())) {
            String content = station.getContainerType().substring(1, station.getContainerType().length() - 1);
            String[] parts = content.split(",");
            Long[] longArray = new Long[parts.length];
            for (int i = 0; i < parts.length; i++) {
                longArray[i] = Long.parseLong(parts[i].trim());
            }
            station.setContainerTypes(longArray);
            List<Long> longs1 = JSONObject.parseArray(station.getContainerType(), Long.class);
            station.setContainerTypes(longs1);
        }
        return R.ok().add(station);
    }
@@ -109,10 +91,10 @@
            return R.error(basStation.getStationName()+"站已被初始化");
        }
        if (null !=basStation.getAreaIds()){
            basStation.setCrossZoneArea(Arrays.toString(basStation.getAreaIds()));
            basStation.setCrossZoneArea(basStation.getAreaIds().toString());
        }
        if (null !=basStation.getContainerTypes()){
            basStation.setContainerType(Arrays.toString(basStation.getContainerTypes()));
            basStation.setContainerType(basStation.getContainerTypes().toString());
        }
        if (!basStationService.save(basStation)) {
            return R.error("保存失败");
@@ -126,11 +108,11 @@
    public R update(@RequestBody BasStation basStation) {
        basStation.setUpdateBy(getLoginUserId());
        basStation.setUpdateTime(new Date());
        if (null !=basStation.getAreaIds()){
            basStation.setCrossZoneArea(Arrays.toString(basStation.getAreaIds()));
        if (null !=basStation.getAreaIds() && !basStation.getContainerTypes().isEmpty()){
            basStation.setCrossZoneArea(basStation.getAreaIds().toString());
        }
        if (null !=basStation.getContainerTypes()){
            basStation.setContainerType(Arrays.toString(basStation.getContainerTypes()));
        if (null != basStation.getContainerTypes() && !basStation.getContainerTypes().isEmpty()){
            basStation.setContainerType(basStation.getContainerTypes().toString());
        }
        if (null !=basStation.getUseStatus() && basStation.getUseStatus().equals(StaUseStatusType.TYPE_O.type)){
            basStation.setBarcode(null);
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
@@ -1,6 +1,7 @@
package com.vincent.rsf.server.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
@@ -14,6 +15,7 @@
import com.vincent.rsf.server.manager.entity.WarehouseAreasItem;
import com.vincent.rsf.server.manager.service.LocItemService;
import com.vincent.rsf.server.system.controller.BaseController;
import com.vincent.rsf.server.system.enums.LocStsType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -48,6 +50,34 @@
    }
    @PreAuthorize("hasAuthority('manager:locItem:list')")
    @PostMapping("/locItem/useO/page")
    public R locUseOPage(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<LocItem, BaseParam> pageParam = new PageParam<>(baseParam, LocItem.class);
        QueryWrapper<LocItem> locItemQueryWrapper = pageParam.buildWrapper(true);
        String applySql = String.format(
                "EXISTS (SELECT 1 FROM man_loc ml " +
                        "WHERE ml.use_status = '%s'" +
                        "AND ml.id = man_loc_item.loc_id " +
                        ")",
                LocStsType.LOC_STS_TYPE_F.type
        );
        locItemQueryWrapper.apply(applySql);
        /**拼接扩展字段*/
        PageParam<LocItem, BaseParam> page = locItemService.page(pageParam, locItemQueryWrapper);
        List<LocItem> records = page.getRecords();
        for (LocItem record : records) {
            if (!Objects.isNull(record.getFieldsIndex())) {
                Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex());
                record.setExtendFields(fields);
            }
        }
        page.setRecords(records);
        return R.ok().add(page);
    }
    @PreAuthorize("hasAuthority('manager:locItem:list')")
    @PostMapping("/locItem/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(locItemService.list());
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemWorkingController.java
New file
@@ -0,0 +1,110 @@
package com.vincent.rsf.server.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
import com.vincent.rsf.server.common.domain.KeyValVo;
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.server.manager.entity.LocItemWorking;
import com.vincent.rsf.server.manager.service.LocItemWorkingService;
import com.vincent.rsf.server.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
public class LocItemWorkingController extends BaseController {
    @Autowired
    private LocItemWorkingService locItemWorkingService;
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @PostMapping("/locItemWorking/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<LocItemWorking, BaseParam> pageParam = new PageParam<>(baseParam, LocItemWorking.class);
        return R.ok().add(locItemWorkingService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @PostMapping("/locItemWorking/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(locItemWorkingService.list());
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @PostMapping({"/locItemWorking/many/{ids}", "/locItemWorkings/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(locItemWorkingService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @GetMapping("/locItemWorking/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(locItemWorkingService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:save')")
    @OperationLog("Create 容器管理")
    @PostMapping("/locItemWorking/save")
    public R save(@RequestBody LocItemWorking locItemWorking) {
        locItemWorking.setCreateBy(getLoginUserId());
        locItemWorking.setCreateTime(new Date());
        locItemWorking.setUpdateBy(getLoginUserId());
        locItemWorking.setUpdateTime(new Date());
        if (!locItemWorkingService.save(locItemWorking)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(locItemWorking);
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:update')")
    @OperationLog("Update 容器管理")
    @PostMapping("/locItemWorking/update")
    public R update(@RequestBody LocItemWorking locItemWorking) {
        locItemWorking.setUpdateBy(getLoginUserId());
        locItemWorking.setUpdateTime(new Date());
        if (!locItemWorkingService.updateById(locItemWorking)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(locItemWorking);
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:remove')")
    @OperationLog("Delete 容器管理")
    @PostMapping("/locItemWorking/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!locItemWorkingService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @PostMapping("/locItemWorking/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<LocItemWorking> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(LocItemWorking::getId, condition);
        }
        locItemWorkingService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:locItemWorking:list')")
    @PostMapping("/locItemWorking/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(locItemWorkingService.list(), LocItemWorking.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/TaskController.java
@@ -31,17 +31,13 @@
    @Autowired
    private TaskService taskService;
    @Autowired
    private TaskItemService taskItemService;
    @Autowired
    private WaitPakinService waitPakinService;
    @Autowired
    private LocService locService;
    @Autowired
    private BasStationService basStationService;
    @PreAuthorize("hasAuthority('manager:task:list')")
    @PostMapping("/task/page")
@@ -103,75 +99,8 @@
        if (Objects.isNull(ids) || ids.length < 1) {
            return R.error("参数不能为空!!");
        }
        List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id);
        List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, ids).in(Task::getTaskStatus, longs));
        if (tasks.isEmpty()) {
            throw new CoolException("任务已处执行状态不可取消!!");
        }
        for (Task task : tasks) {
            //恢复组托状态
            WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                    .eq(WaitPakin::getBarcode, task.getBarcode())
            );
            if (null != waitPakin) {
                waitPakin.setIoStatus(Short.valueOf(PakinIOStatus.PAKIN_IO_STATUS_DONE.val));
                if (!waitPakinService.updateById(waitPakin)) {
                    throw new CoolException("更新组托状态失败!!");
                }
            }
            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
                    .eq(Loc::getCode, task.getTaskType() < 100 ? task.getTargLoc() : task.getOrgLoc())
            );
            if (null != loc
                    && (loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type)
                    || loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type))) {
                loc.setUseStatus(LocStsType.LOC_STS_TYPE_O.type);
                if (!locService.updateById(loc)) {
                    throw new CoolException("更新库位状态失败!!");
                }
            }
            if (task.getWarehType().equals(WarehType.WAREHOUSE_TYPE_AGV.id)){
                BasStation basStation = null;
                if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_IN.type)
                ){
                    basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                            .eq(BasStation::getStationName, task.getOrgSite())
                            .eq(BasStation::getUseStatus, StaUseStatusType.TYPE_R.type)
                    );
                } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_IN.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_OUT.type)
                ) {
                    basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                            .eq(BasStation::getStationName, task.getTargLoc())
                            .eq(BasStation::getUseStatus, StaUseStatusType.TYPE_R.type)
                    );
                }
                if (null == basStation) {
                    throw new CoolException("站点状态错误!!");
                }
                basStation.setUseStatus(StaUseStatusType.TYPE_F.type);
                if (!basStationService.updateById(basStation)){
                    throw new CoolException("更新站点状态失败!!");
                }
            }
        }
        if (!taskService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        if (!taskItemService.remove(new LambdaQueryWrapper<TaskItem>().in(TaskItem::getTaskId, ids))) {
            return R.error("Details delete Failed");
        }
        return R.ok("Delete Success").add(ids);
       return taskService.removeTask(ids);
//        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:task:list')")
@@ -203,30 +132,44 @@
        }
        List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id);
        List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getId, id).in(Task::getTaskStatus, longs));
//        if (tasks.isEmpty()) {
//            throw new CoolException("任务已处执行状态不可一键完成!!");
//        }
//        taskService.completeTask(tasks);
        for (Task task : tasks) {
            task.setTaskStatus(task.getTaskType() < (short)100 ? TaskStsType.COMPLETE_IN.id : TaskStsType.COMPLETE_OUT.id);
            task.setTaskStatus(task.getTaskType() < (short) 100 ? TaskStsType.COMPLETE_IN.id : TaskStsType.COMPLETE_OUT.id);
        }
        if (!taskService.updateBatchById(tasks)){
        if (!taskService.updateBatchById(tasks)) {
            return R.error("完成任务失败");
        }
        return R.ok("完成任务成功");
    }
    /**
     * 拣料出库
     * @return
     */
    @PreAuthorize("hasAuthority('manager:task:update')")
    @ApiOperation("取消任务")
    @PostMapping("/task/cancel/{id}")
    public R cancelTask(@PathVariable String id) {
    @ApiOperation("拣料出库")
    @PostMapping("/task/pick/{id}")
    public R pickTask(@PathVariable Long id) {
        if (Objects.isNull(id)) {
            throw new CoolException("参数不能为空!!");
            throw new CoolException("能数不能为空!!");
        }
        return R.ok();
        try {
            return taskService.pickTask(id);
        } catch (Exception e) {
            throw new CoolException(e.getMessage());
        }
    }
//    @PreAuthorize("hasAuthority('manager:task:update')")
//    @ApiOperation("取消任务")
//    @PostMapping("/task/cancel/{id}")
//    public R cancelTask(@PathVariable String id) {
//        if (Objects.isNull(id)) {
//            throw new CoolException("参数不能为空!!");
//        }
//        return R.ok();
//    }
    @PreAuthorize("hasAuthority('manager:task:update')")
    @ApiOperation("任务出库置顶")
    @PostMapping("/task/top/{id}")
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/BasStation.java
@@ -4,6 +4,7 @@
import java.util.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.server.manager.service.WarehouseAreasService;
@@ -150,10 +151,10 @@
    private Long tenantId;
    @TableField(exist = false)
    private Long[] areaIds;
    private List<Long> areaIds;
    @TableField(exist = false)
    private Long[] containerTypes;
    private List<Long> containerTypes;
    public BasStation() {}
@@ -185,16 +186,11 @@
        }
        DictDataService service = SpringUtils.getBean(DictDataService.class);
        String content = this.getContainerType().substring(1, this.getContainerType().length() - 1);
        String[] parts = content.split(",");
        Long[] longArray = new Long[parts.length];
        for (int i = 0; i < parts.length; i++) {
            longArray[i] = Long.parseLong(parts[i].trim());
        }
        Object parse = JSONArray.parse(this.getContainerType());
        List<Long> longs1 = JSONObject.parseArray(parse.toString(), Long.class);
        List<DictData> dictData = service.list(new LambdaQueryWrapper<DictData>()
                .eq(DictData::getDictTypeCode, "sys_container_type")
                .in(DictData::getValue, longArray)
                .in(DictData::getValue, longs1)
        );
        List<Long> longs = dictData.stream().map(DictData::getId).collect(Collectors.toList());
        return longs;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItemWorking.java
New file
@@ -0,0 +1,368 @@
package com.vincent.rsf.server.manager.entity;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.text.SimpleDateFormat;
import java.util.Date;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@Accessors(chain = true)
@TableName("man_loc_item_working")
public class LocItemWorking implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 主单ID
     */
    @ApiModelProperty(value= "主单ID")
    private Long locId;
    @ApiModelProperty("任务明细ID")
    private Long taskId;
    /**
     * 库位
     */
    @ApiModelProperty(value= "库位")
    private String locCode;
    /**
     * 单据ID
     */
    @ApiModelProperty(value= "单据ID")
    private Long orderId;
    /**
     * 单据类型
     */
    @ApiModelProperty(value= "单据类型")
    private String type;
    /**
     * 订单明细id
     */
    @ApiModelProperty(value= "订单明细id")
    private Long orderItemId;
    /**
     * 业务类型
     */
    @ApiModelProperty(value= "业务类型")
    private Short wkType;
    /**
     * 物料ID
     */
    @ApiModelProperty(value= "物料ID")
    private Long matnrId;
    /**
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String maktx;
    /**
     * 平台ID(行号)
     */
    @ApiModelProperty(value= "平台ID(行号)")
    private String platItemId;
    /**
     * 客户订单号
     */
    @ApiModelProperty(value= "客户订单号")
    private String platOrderCode;
    /**
     * 工单号
     */
    @ApiModelProperty(value= "工单号")
    private String platWorkCode;
    /**
     * 项目号
     */
    @ApiModelProperty(value= "项目号")
    private String projectCode;
    /**
     * 物料编码
     */
    @ApiModelProperty(value= "物料编码")
    private String matnrCode;
    /**
     * 物料跟踪码
     */
    @ApiModelProperty(value= "物料跟踪码")
    private String trackCode;
    /**
     * 库存单位
     */
    @ApiModelProperty(value= "库存单位")
    private String unit;
    /**
     * 数量
     */
    @ApiModelProperty(value= "数量")
    private Double anfme;
    /**
     * 完成数量
     */
    @ApiModelProperty(value= "完成数量")
    private Double qty;
    /**
     * 执行数量
     */
    @ApiModelProperty(value= "执行数量 ")
    private Double workQty;
    /**
     * 供应商批次
     */
    @ApiModelProperty(value= "供应商批次")
    private String batch;
    /**
     * 供应商批次(原供应商批次,暂停使用)
     */
    @ApiModelProperty(value= "供应商批次(原供应商批次,暂停使用)")
    private String splrBatch;
    /**
     * 供应商ID
     */
    @ApiModelProperty(value= "供应商ID")
    private Long splrId;
    /**
     * 规格
     */
    @ApiModelProperty(value= "规格")
    private String spec;
    /**
     * 型号
     */
    @ApiModelProperty(value= "型号")
    private String model;
    /**
     * 字段索引
     */
    @ApiModelProperty(value= "字段索引")
    private String fieldsIndex;
    /**
     * 状态 1: 正常  0: 冻结
     */
    @ApiModelProperty(value= "状态 1: 正常  0: 冻结  ")
    private Integer status;
    /**
     * 是否删除 1: 是  0: 否
     */
    @ApiModelProperty(value= "是否删除 1: 是  0: 否  ")
    @TableLogic
    private Integer deleted;
    /**
     * 租户
     */
    @ApiModelProperty(value= "租户")
    private Integer tenantId;
    /**
     * 添加人员
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * 添加时间
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 修改人员
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * 修改时间
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public LocItemWorking() {}
    public LocItemWorking(Long locId,String locCode,Long orderId,String type,Long orderItemId,Short wkType,Long matnrId,String maktx,String platItemId,String platOrderCode,String platWorkCode,String projectCode,String matnrCode,String trackCode,String unit,Double anfme,Double qty,Double workQty,String batch,String splrBatch,Long splrId,String spec,String model,String fieldsIndex,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.locId = locId;
        this.locCode = locCode;
        this.orderId = orderId;
        this.type = type;
        this.orderItemId = orderItemId;
        this.wkType = wkType;
        this.matnrId = matnrId;
        this.maktx = maktx;
        this.platItemId = platItemId;
        this.platOrderCode = platOrderCode;
        this.platWorkCode = platWorkCode;
        this.projectCode = projectCode;
        this.matnrCode = matnrCode;
        this.trackCode = trackCode;
        this.unit = unit;
        this.anfme = anfme;
        this.qty = qty;
        this.workQty = workQty;
        this.batch = batch;
        this.splrBatch = splrBatch;
        this.splrId = splrId;
        this.spec = spec;
        this.model = model;
        this.fieldsIndex = fieldsIndex;
        this.status = status;
        this.deleted = deleted;
        this.tenantId = tenantId;
        this.createBy = createBy;
        this.createTime = createTime;
        this.updateBy = updateBy;
        this.updateTime = updateTime;
        this.memo = memo;
    }
//    LocItemWorking locItemWorking = new LocItemWorking(
//            null,    // 主单ID
//            null,    // 库位
//            null,    // 单据ID
//            null,    // 单据类型
//            null,    // 订单明细id
//            null,    // 业务类型
//            null,    // 物料ID
//            null,    // 物料名称
//            null,    // 平台ID(行号)
//            null,    // 客户订单号
//            null,    // 工单号
//            null,    // 项目号
//            null,    // 物料编码
//            null,    // 物料跟踪码
//            null,    // 库存单位
//            null,    // 数量
//            null,    // 完成数量
//            null,    // 执行数量
//            null,    // 供应商批次
//            null,    // 供应商批次(原供应商批次,暂停使用)
//            null,    // 供应商ID
//            null,    // 规格
//            null,    // 型号
//            null,    // 字段索引
//            null,    // 状态[非空]
//            null,    // 是否删除[非空]
//            null,    // 租户
//            null,    // 添加人员
//            null,    // 添加时间[非空]
//            null,    // 修改人员
//            null,    // 修改时间[非空]
//            null    // 备注
//    );
    public String getStatus$(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "冻结";
            default:
                return String.valueOf(this.status);
        }
    }
    public String getCreateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getUpdateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Task.java
@@ -56,6 +56,8 @@
    @ApiModelProperty(value= "任务状态")
    private Short taskStatus;
    @ApiModelProperty("上级任务ID")
    private Long parentId;
    @ApiModelProperty("仓库类型")
    private Short warehType;
@@ -253,7 +255,7 @@
        if (Objects.isNull(dictDatas) || Objects.isNull(dictDatas.getLabel())) {
            return null;
        }
        return dictDatas.getLabel();
        return dictDatas.getValue() + "." + dictDatas.getLabel();
    }
    public String getTaskType$() {
@@ -267,7 +269,7 @@
        if (Objects.isNull(dictDatas) || Objects.isNull(dictDatas.getLabel())) {
            return null;
        }
        return dictDatas.getLabel();
        return dictDatas.getValue() + "." + dictDatas.getLabel();
    }
    public String getStartTime$(){
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/LocType.java
File was renamed from rsf-server/src/main/java/com/vincent/rsf/server/system/enums/LocType.java
@@ -1,4 +1,4 @@
package com.vincent.rsf.server.system.enums;
package com.vincent.rsf.server.manager.enums;
/**
 * @author Ryan
@@ -19,7 +19,7 @@
        this.desc = desc;
    }
    private String type;
    public String type;
    private String desc;
    public String desc;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemWorkingMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.LocItemWorking;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface LocItemWorkingMapper extends BaseMapper<LocItemWorking> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
@@ -144,10 +144,5 @@
        if (!taskItemService.removeByIds(itemIds)) {
            throw new CoolException("原始任务明细删除失败!!");
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemWorkingService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.LocItemWorking;
public interface LocItemWorkingService extends IService<LocItemWorking> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java
@@ -19,4 +19,7 @@
//    R completeTask(String id);
     void completeTask(List<Task> task) throws Exception;
    R removeTask(Long[] ids);
    R pickTask(Long id) throws Exception;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemWorkingServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.LocItemWorkingMapper;
import com.vincent.rsf.server.manager.entity.LocItemWorking;
import com.vincent.rsf.server.manager.service.LocItemWorkingService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("locItemWorkingService")
public class LocItemWorkingServiceImpl extends ServiceImpl<LocItemWorkingMapper, LocItemWorking> implements LocItemWorkingService {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.server.api.controller.params.TaskInParam;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
@@ -15,7 +16,9 @@
import com.vincent.rsf.server.manager.utils.LocManageUtil;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.enums.LocStsType;
import com.vincent.rsf.server.manager.enums.LocType;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -60,6 +63,12 @@
    private WaveItemService waveItemService;
    @Autowired
    private WaveService waveService;
    @Autowired
    private BasStationService basStationService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private LocItemWorkingService locItemWorkingService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -369,18 +378,222 @@
     * @param tasks
     * @throws Exception
     */
    @Synchronized
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void completeTask(List<Task> tasks) throws Exception {
    public void completeTask(List<Task> tasks) throws Exception {
        for (Task task : tasks) {
            //出库任务
            if (task.getTaskType() < TaskType.TASK_TYPE_OUT.type) {
                //入库任务
                complateInstock(task);
                if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type)) {
                    //1.入库
                    complateInstock(task);
                } else if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type)) {
                    //53.拣料再入库
                    pickComplateInStock(task);
                }
            } else {
                //出库任务
                complateOutStock(task);
                if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                    //全托出库
                    complateOutStock(task);
                } else if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_IN.type)) {
                    //拣料出库
                    pickTask(task.getId());
                    //移除原始库存
                    complateOutStock(task);
                }
            }
        }
    }
    /**
     * 拣料再入库
     *
     * @param task
     */
    @Transactional(rollbackFor = Exception.class)
    public void pickComplateInStock(Task task) throws Exception {
        if (Objects.isNull(task)) {
            return;
        }
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getTargLoc()));
        if (Objects.isNull(loc)) {
            throw new CoolException("库存不存在!!");
        }
        if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type)) {
            throw new CoolException("当前库位状态不处于S.入库预约,不可执行入库操作!");
        }
        loc.setUseStatus(LocStsType.LOC_STS_TYPE_F.type).setBarcode(task.getBarcode());
        if (!locService.updateById(loc)) {
            throw new CoolException("库位信息更新失败!!");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        if (taskItems.isEmpty()) {
            throw new CoolException("任务明细不存在!!");
        }
        List<LocItem> items = new ArrayList<>();
        for (TaskItem taskItem : taskItems) {
            LocItem locItem = new LocItem();
            LocItemWorking locWorking = locItemWorkingService.getOne(new LambdaQueryWrapper<LocItemWorking>()
                    .eq(LocItemWorking::getTaskId, taskItem.getTaskId())
                    .eq(LocItemWorking::getLocCode, task.getTargLoc())
                    .eq(LocItemWorking::getMatnrId, taskItem.getMatnrId())
                    .eq(StringUtils.isNoneBlank(taskItem.getFieldsIndex()), LocItemWorking::getFieldsIndex, taskItem.getFieldsIndex())
                    .eq(StringUtils.isNotBlank(taskItem.getBatch()), LocItemWorking::getBatch, taskItem.getBatch()));
            if (Objects.isNull(locWorking)) {
                throw new CoolException("数据错误,作业中库存数据丢失!!");
            }
            if (locWorking.getAnfme().compareTo(taskItem.getAnfme()) >= 0) {
                locWorking.setAnfme(Math.round((locWorking.getAnfme() - taskItem.getAnfme()) * 10000) / 10000.0);
            } else {
                continue;
            }
            BeanUtils.copyProperties(locWorking, locItem);
            locItem.setLocCode(loc.getCode()).setLocId(loc.getId()).setId(null);
            items.add(locItem);
        }
        if (!locItemService.saveBatch(items)) {
            throw new CoolException("作业库存回写失败!!");
        }
        if (!locItemWorkingService.remove(new LambdaQueryWrapper<LocItemWorking>().eq(LocItemWorking::getTaskId, task.getId()))) {
            throw new CoolException("作业中库存删除失败!!");
        }
    }
    /**
     * 任务取消
     *
     * @param ids
     * @return
     */
    @Override
    public R removeTask(Long[] ids) {
        List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id);
        List<Task> tasks = this.list(new LambdaQueryWrapper<Task>().in(Task::getId, ids).in(Task::getTaskStatus, longs));
        if (tasks.isEmpty()) {
            throw new CoolException("任务已处执行状态不可取消!!");
        }
        for (Task task : tasks) {
            //恢复组托状态
            WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                    .eq(WaitPakin::getBarcode, task.getBarcode())
            );
            if (null != waitPakin) {
                waitPakin.setIoStatus(Short.valueOf(PakinIOStatus.PAKIN_IO_STATUS_DONE.val));
                if (!waitPakinService.updateById(waitPakin)) {
                    throw new CoolException("更新组托状态失败!!");
                }
            }
            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getTaskType() < TaskStsType.UPDATED_IN.id ? task.getTargLoc() : task.getOrgLoc()));
            if (null != loc
                    && (loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type)
                    || loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type))) {
                loc.setUseStatus(LocStsType.LOC_STS_TYPE_O.type);
                if (!locService.updateById(loc)) {
                    throw new CoolException("更新库位状态失败!!");
                }
            }
            if (!Objects.isNull(task.getWarehType()) && task.getWarehType().equals(WarehType.WAREHOUSE_TYPE_AGV.id)) {
                BasStation basStation = null;
                if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type) || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_IN.type)) {
                    basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                            .eq(BasStation::getStationName, task.getOrgSite())
                            .eq(BasStation::getUseStatus, StaUseStatusType.TYPE_R.type)
                    );
                } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_IN.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_OUT.type)
                ) {
                    basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                            .eq(BasStation::getStationName, task.getTargLoc())
                            .eq(BasStation::getUseStatus, StaUseStatusType.TYPE_R.type)
                    );
                }
                if (null == basStation) {
                    throw new CoolException("站点状态错误!!");
                }
                basStation.setUseStatus(StaUseStatusType.TYPE_F.type);
                if (!basStationService.updateById(basStation)) {
                    throw new CoolException("更新站点状态失败!!");
                }
            }
        }
        if (!this.removeByIds(Arrays.asList(ids))) {
            throw new CoolException("Delete Fail");
        }
        return R.ok("取消完成");
    }
    /**
     * 拣料出库
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R pickTask(Long id) throws Exception {
        Task task = this.getById(id);
        if (Objects.isNull(task)) {
            throw new CoolException("当前任务不存在!!");
        }
        if (!task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_IN.type)) {
            throw new CoolException("非拣料出库 ,不可执行此操作!!");
        }
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getCode, task.getOrgLoc()));
        if (Objects.isNull(loc)) {
            throw new CoolException("没有空库位!!");
        }
        List<LocItem> locItems = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, loc.getId()));
        if (locItems.isEmpty()) {
            throw new CoolException("库位明细不存在!!");
        }
        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, task);
        //更新任务主单
        task.setTaskCode(ruleCode)
                .setTaskType(TaskType.TASK_TYPE_PICK_IN.type)
                .setBarcode(task.getBarcode())
                .setTaskStatus(TaskStsType.GENERATE_IN.id);
        //TODO 后续需根据仓库类型查找新库位,原始库位置空闲状态 {
        // TaskInParam param = new TaskInParam();
        //        param.setSourceStaNo(Integer.parseInt(task.getOrgSite()))
        //                .setIoType(Integer.parseInt(TaskType.TASK_TYPE_IN.type + ""));
        ////                .setLocType1(LocType.LOC_TYPE_LOW.type);}
        if (!this.updateById(task)) {
            throw new CoolException("任务状态更新失败!!");
        }
        List<LocItemWorking> workings = new ArrayList<>();
        for (LocItem item : locItems) {
            LocItemWorking working = new LocItemWorking();
            BeanUtils.copyProperties(item, working);
            working.setId(null).setTaskId(task.getId());
            workings.add(working);
        }
        if (!locItemWorkingService.saveBatch(workings)) {
            throw new CoolException("临时库存保存失败!!");
        }
        return R.ok("拣货成功!!");
    }
    /**
@@ -389,20 +602,29 @@
     * @description: 完成出库任务,更新出库库存信息
     * @version 1.0
     */
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    public synchronized void complateOutStock(Task task) {
    public void complateOutStock(Task task) throws Exception {
        if (Objects.isNull(task)) {
            return;
            throw new CoolException("参数不能为空!!");
        }
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getOrgLoc()));
        if (Objects.isNull(loc)) {
            throw new CoolException("库位不存在!!");
        }
        if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) {
            throw new CoolException("库位状态不处理于R.出库预约!!");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        if (taskItems.isEmpty()) {
            throw new CoolException("任务明细不存在!!");
        }
        try {
            //更新库位明细
            subtractLocItem(taskItems, task.getId());
            subtractLocItem(loc);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
            throw new CoolException(e.getMessage());
        }
        //添加出入库记录信息
@@ -433,13 +655,9 @@
            }
        }
        /**修改库位状态为F.在库*/
        List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getTargLoc()));
        if (locs.isEmpty()) {
            //如单据明细为空,修改为库位状态为O.空库
            if (!locService.update(new LambdaUpdateWrapper<Loc>().set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type).eq(Loc::getCode, task.getTargLoc()))) {
                throw new CoolException("库位状态修改失败!!");
            }
        /**修改为库位状态为O.空库*/
        if (!locService.update(new LambdaUpdateWrapper<Loc>().set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type).eq(Loc::getCode, loc.getId()))) {
            throw new CoolException("库位状态修改失败!!");
        }
        if (!this.update(new LambdaUpdateWrapper<Task>().eq(Task::getId, task.getId()).set(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id))) {
            throw new CoolException("任务状态修改失败!!");
@@ -497,34 +715,9 @@
     * @version 1.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void subtractLocItem(List<TaskItem> items, Long taskId) throws Exception {
        Task task = this.getById(taskId);
        if (Objects.isNull(task)) {
            throw new CoolException("任务不存在!!");
        }
        for (TaskItem item : items) {
            LocItem locItem = locItemService.getOne(new LambdaQueryWrapper<LocItem>()
                    .eq(LocItem::getBatch, item.getBatch())
                    .eq(LocItem::getFieldsIndex, item.getFieldsIndex())
                    .eq(LocItem::getMatnrId, item.getMatnrId())
                    .eq(LocItem::getLocCode, task.getTargLoc()));
            if (Objects.isNull(locItem)) {
                throw new CoolException("库存明细不存在!!");
            }
            //剩余库存
            Double minuValue = (Math.round((locItem.getAnfme() - locItem.getWorkQty() - locItem.getQty()) * 10000.0) / 10000.0);
            if (minuValue.compareTo(item.getAnfme()) <= 0) {
                //剩余库存小于出库库存,移除当前库存信息
                if (!locItemService.removeById(locItem.getId())) {
                    throw new CoolException("库存删除失败!!");
                }
            } else {
                locItem.setWorkQty((Math.round((locItem.getWorkQty() - minuValue) * 10000.0) / 10000.0));
                locItem.setQty((Math.round((minuValue + locItem.getQty()) * 10000.0) / 10000.0));
                if (!locItemService.updateById(locItem)) {
                    throw new CoolException("库存信息更新失败!!");
                }
            }
    public void subtractLocItem(Loc loc) throws Exception {
        if (!locItemService.remove(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, loc.getId()))) {
            throw new CoolException("库存明细删除失败!!");
        }
    }
@@ -539,6 +732,16 @@
        if (Objects.isNull(task)) {
            return;
        }
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getTargLoc()));
        if (Objects.isNull(loc)) {
            throw new CoolException("目标库位不存在!");
        }
        if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type)) {
            throw new CoolException("当前库位状态不处于S.入库预约,不可执行入库操作!");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        if (taskItems.isEmpty()) {
            throw new CoolException("任务明细不存在!!");
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaitPakinServiceImpl.java
@@ -298,8 +298,7 @@
                    continue;
                }
                double sum = pakin.stream().mapToDouble(WaitPakinItem::getAnfme).sum();
                item.setWorkQty(item.getWorkQty() - sum)
                        .setAnfme(item.getAnfme() + sum);
                item.setWorkQty(item.getWorkQty() - sum);
                if (!warehouseAreasItemService.updateById(item)) {
                    throw new CoolException("收货区数据回滚失败!!");
                }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -17,6 +17,7 @@
import com.vincent.rsf.server.manager.utils.OptimalAlgorithmUtil;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -101,8 +102,9 @@
     * @description 生成出库任务
     * @time 2025/4/28 14:01
     */
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    private synchronized void generateOutTask(List<WaveItem> itemParams, Long loginUserId, Wave wave) throws Exception {
    public void generateOutTask(List<WaveItem> itemParams, Long loginUserId, Wave wave) throws Exception {
        List<LocItem> locItemList = new ArrayList<>();
        for (WaveItem param : itemParams) {
            String locs = param.getStockLocs();
@@ -246,7 +248,8 @@
     * @description 根据物料编码,批次,动态字段 查询符合的库位,再根据库位中物料的数量选择最适合的库位
     * @time 2025/4/27 09:26
     */
    private synchronized List<WaveItem> getLocs(List<WaveItem> waveItems) throws Exception {
    @Synchronized
    private  List<WaveItem> getLocs(List<WaveItem> waveItems) throws Exception {
        //TODO  根据物料编码,批次,动态字段 查询符合的库位,再根据库位中物料的数量选择最适合的库位
        waveItems.forEach(waveItem -> {
            List<LocItem> locItems = locItemService.list(new QueryWrapper<LocItem>()
rsf-server/src/main/resources/mapper/manager/LocItemWorkingMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.LocItemWorkingMapper">
</mapper>