skyouc
2 天以前 f695b59337121fb6c93251e73bdfb3fc8e847cc8
调拔单功能优化
13个文件已修改
4个文件已添加
1个文件已删除
1674 ■■■■ 已修改文件
rsf-admin/src/i18n/en.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/transfer/CreateBySelectMats.jsx 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/transfer/ManualCreate.jsx 469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/transfer/TransferCreate.jsx 401 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/transfer/TransferItemEdit.jsx 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/transfer/TransferList.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/TransferController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/TransferItemParams.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Transfer.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/OrderSourceType.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/OrderType.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/TransferType.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TransferService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/MatnrServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TransferServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -1270,6 +1270,7 @@
        quality: "quality",
        complete: "complete",
        allComfirm: 'All Comfirm',
        createTransfer: 'Create Transfer Order',
        verifyComfirm: 'Verify Comfirm',
        close: "close",
        asnCreate: "Create By Order",
rsf-admin/src/i18n/zh.js
@@ -1282,6 +1282,7 @@
        quality: "质检",
        complete: "完结",
        close: "关闭",
        createTransfer: '创建调拔单',
        asnCreate: "通过单据创建",
        poCreate: "通过PO单创建",
        orderPrint: '打印单据',
rsf-admin/src/page/orders/transfer/CreateBySelectMats.jsx
New file
@@ -0,0 +1,300 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    useNotify,
    useRefresh,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { Stack, Grid, Box, Typography, Dialog, DialogTitle, DialogContent, TextField, Button, DialogActions } from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton.jsx";
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import StatusSelectInput from "../../components/StatusSelectInput";
import CustomerTopToolBar from "../../components/EditTopToolBar";
import TreeSelectInput from "@/page/components/TreeSelectInput";
import EditBaseAside from "../../components/EditBaseAside";
import MemoInput from "../../components/MemoInput";
import SaveIcon from '@mui/icons-material/Save';
import { DataGrid } from '@mui/x-data-grid';
import * as Common from '@/utils/common';
import request from '@/utils/request';
const CreateBySelectMats = (props) => {
    const { open, setOpen, data, setData } = props;
    const [page, setPage] = useState(0);
    const [rowCount, setRowCount] = useState(0);
    const [formData, setFormData] = useState({});
    const [tableData, setTableData] = useState([]);
    const [dyFields, setDyFields] = useState([]);
    const [pageSize, setPageSize] = useState(25);
    const [selectedRows, setSelectedRows] = useState([]);
    const translate = useTranslate();
    const notify = useNotify();
    const refresh = useRefresh();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(() => ({
            [name]: value
        }));
    };
    const reset = () => {
        setFormData({
            name: '',
            code: '',
            groupId: 0
        })
    }
    const handleSubmit = () => {
        const hasarr = data.map(el => +el.matnrId)
        const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id)));
        const value = selectedData.map((el => {
            const dynamicFields = dyFields.reduce((acc, item) => {
                acc[item.fields] = el['extendFields']?.[item.fields] || '';
                return acc;
            }, {});
            return {
                matnrId: el.id,
                maktx: el.name,
                matnrCode: el.code,
                stockUnit: el.stockUnit || '',
                purUnit: el.purchaseUnit || '',
                ...dynamicFields
            }
        }))
        setData([...data, ...value]);
        setOpen(false);
        reset();
    };
    const getData = async () => {
        const res = await request.post(`/matnr/page`, {
            ...formData,
            current: page,
            pageSize: pageSize,
            orderBy: "create_time desc"
        });
        if (res?.data?.code === 200) {
            const {data} = res.data;
            setTableData(data?.records);
            setRowCount(data?.total)
            console.log(rowCount);
            console.log(data);
        } 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={3}>
                            <TextField
                                label={translate('table.field.matnr.name')}
                                name="name"
                                value={formData.name}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                        <Grid item md={3}>
                            <TextField
                                label={translate('table.field.matnr.code')}
                                name="code"
                                value={formData.code}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                        <Grid item md={3}>
                            <TreeSelectInput
                                label="table.field.matnr.groupId"
                                value={formData.groupId}
                                resource={'matnrGroup'}
                                source="groupId"
                                name="groupId"
                                onChange={handleChange}
                            />
                        </Grid>
                        <Grid item md={2} sx={{ margin: 'auto' }}>
                            <Button variant="contained" onClick={handleSearch}>{translate('toolbar.query')}</Button>
                        </Grid>
                    </Grid>
                </Box>
                <Box sx={{ mt: 2, height: 400, width: '100%' }}>
                    <SelectMatsTableView
                        tableData={tableData}
                        setTableData={setTableData}
                        page={page}
                        rowCount={rowCount}
                        pageSize={pageSize}
                        setPage={setPage}
                        setPageSize={setPageSize}
                        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: 'flex-end' }}>
                    <Button onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
                        {translate('toolbar.confirm')}
                    </Button>
                </Box>
            </DialogActions>
        </Dialog>
    );
}
export default CreateBySelectMats;
const SelectMatsTableView = ({ tableData, page, pageSize,setPage, setPageSize, rowCount, setTableData, selectedRows, setSelectedRows, dyFields, setDyFields }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [extendColumns, setExtendColumns] = useState([]);
    const [columns, setColumns] = useState([
        { field: 'name', headerName: translate('table.field.matnr.name'), width: 300 },
        { field: 'code', headerName: translate('table.field.matnr.code'), width: 200 },
        { field: 'groupId$', headerName: translate('table.field.matnr.groupId'), width: 100 },
        { field: 'spec', headerName: translate('table.field.matnr.spec'), width: 100 },
        { field: 'model', headerName: translate('table.field.matnr.model'), width: 100 },
        { field: 'weight', headerName: translate('table.field.matnr.weight'), width: 100 },
        { field: 'describle', headerName: translate('table.field.matnr.describle'), width: 100 },
        { field: 'nromNum', headerName: translate('table.field.matnr.nromNum'), width: 100 },
        { field: 'unit', headerName: translate('table.field.matnr.unit'), width: 100 },
        { field: 'purchaseUnit', headerName: translate('table.field.matnr.purUnit'), width: 100 },
        { field: 'stockUnit', headerName: translate('table.field.matnr.stockUnit'), width: 100 },
        { field: 'stockLeval$', headerName: translate('table.field.matnr.stockLevel'), width: 100, sortable: false },
    ])
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    useEffect(() => {
        if (extendColumns == undefined || extendColumns.length < 1) {
            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] || '';
                },
            }))
            setExtendColumns(cols);
            setDyFields(data)
            setColumns([...columns, ...cols])
        } else {
            notify(msg);
        }
    }
    return (
        <div style={{ height: 400, width: '100%' }}>
            <DataGrid
                size="small"
                rows={tableData}
                page={page}
                pageSize={pageSize}
                columns={columns}
                pagination
                checkboxSelection
                rowCount={rowCount}
                onRowSelectionModelChange={handleSelectionChange}
                onPageChange={(newPage) => setPage(newPage)}
                onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
                selectionModel={selectedRows}
                paginationMode="server"
                disableColumnMenu={true}
                initialState={{
                    pagination: {
                        paginationModel: {
                            pageSize: 25,
                        },
                    },
                }}
                pageSizeOptions={[15, 25, 50, 100]}
                disableColumnSorting
                disableMultipleColumnsSorting
            />
        </div>
    );
};
rsf-admin/src/page/orders/transfer/ManualCreate.jsx
New file
@@ -0,0 +1,469 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    TextField,
    IconButton,
    MenuItem,
    Tooltip,
    Select,
    Button,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton.jsx";
import ConfirmationNumberIcon from '@mui/icons-material/ConfirmationNumber';
import CreateBySelectMats from "./CreateBySelectMats.jsx";
import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
import { Add, Edit, Delete } from '@mui/icons-material';
import ConfirmButton from "../../components/ConfirmButton";
import DictSelect from "../../components/DictSelect";
import { minHeight, padding } from "@mui/system";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
const ManualCreate = (props) => {
    const { open, setOpen, orderId } = props;
    const tableRef = useRef();
    const notify = useNotify();
    const translate = useTranslate();
    const [tabelData, setTableData] = useState([]);
    const [disabled, setDisabled] = useState(false);
    const [selectedRows, setSelectedRows] = useState([]);
    const [createDialog, setCreateDialog] = useState(false);
    const [formData, setFormData] = useState({});
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSubmit = async () => {
        setFinally()
        setDisabled(true)
        if (asnId === 0) {
            const parmas = {
                "orders": formData,
                "items": tabelData,
            }
            const res = await request.post(`/outStock/items/save`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                resetData()
            } else {
                notify(res.data.msg);
            }
        } else {
            const parmas = {
                "orders": formData,
                "items": tabelData,
            }
            const res = await request.post(`/outStock/items/update`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                resetData()
            } else {
                notify(res.data.msg);
            }
        }
        setDisabled(false)
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleDeleteItem = () => {
        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
        setTableData(newTableData);
    }
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <Box sx={{ padding: 1 }}>
                <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                    <Form defaultValues={formData}>
                        <Grid container spacing={2}>
                            <Grid item md={2}>
                                <DictSelect
                                    label={translate("table.field.transfer.type")}
                                    value={formData.wkType}
                                    variant="filled"
                                    group='2'
                                    onChange={(e) => handleChange(e.target.value, 'wkType')}
                                    dictTypeCode="sys_business_type"
                                    required
                                />
                            </Grid>
                            <Grid item md={2}>
                                <ReferenceInput reference="warehouseAreas" source="name">
                                    <AutocompleteInput optionText="name" label="table.field.transfer.orgAreaName" />
                                </ReferenceInput>
                            </Grid>
                            <Grid item md={2}>
                                <ReferenceInput reference="warehouseAreas" source="name">
                                    <AutocompleteInput optionText="name" label="table.field.transfer.orgAreaName" />
                                </ReferenceInput>
                            </Grid>
                        </Grid>
                    </Form>
                </Box>
                <Box sx={{ mt: 2 }}>
                    <Stack direction="row" spacing={2} sx={{ justifyContent: "flex-end" }}>
                        <Button variant="contained" onClick={() => setCreateDialog(true)} >
                            {translate('common.action.newAddMats')}
                        </Button>
                        <ConfirmButton label={"toolbar.delete"} variant="outlined" color="error" onConfirm={handleDeleteItem} />
                    </Stack>
                </Box>
                <Box sx={{ mt: 2 }}>
                    <TransferTableView
                        tabelData={tabelData}
                        setTableData={setTableData}
                        asnId={orderId}
                        selectedRows={selectedRows}
                        setSelectedRows={setSelectedRows}
                        tableRef={tableRef}>
                    </TransferTableView>
                </Box>
                <Toolbar sx={{ width: '100%', justifyContent: 'flex-end', bgcolor: 'white' }}  >
                    <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
                        {translate('toolbar.confirm')}
                    </Button>
                </Toolbar>
                <CreateBySelectMats
                    open={createDialog}
                    setOpen={setCreateDialog}
                    data={tabelData}
                    setData={setTableData}
                />
            </Box>
        </>
    )
}
const SelectInputSplrNameEditCell = (params) => {
    const [formData, setFormData] = useState([{}])
    useEffect(() => {
        getOptions();
    }, []);
    const getOptions = async () => {
        const parmas = {
            "type": "supplier"
        }
        const {
            data: { code, data, msg },
        } = await request.post("companys/page", parmas);
        if (code === 200) {
            setFormData(data.records)
        } else {
            notify(msg);
        }
    }
    return (
        <Select
            value={params.value}
            onChange={(e) => {
                params.api.setEditCellValue({
                    id: params.id,
                    field: params.field,
                    value: e.target.value,
                })
                // 找到选中的供应商记录
                const selectedSupplier = formData.find(supplier => supplier.name === e.target.value);
                // 如果找到对应的供应商记录,同时更新splrCode字段
                if (selectedSupplier) {
                    params.api.setEditCellValue({
                        id: params.id,
                        field: 'splrCode',
                        value: selectedSupplier.id,
                    });
                }
            }}
            fullWidth
        >
            {formData.map(e => {
                return (
                    <MenuItem value={e.name} children={e.name} key={e.id} />
                );
            })}
        </Select>
    );
};
const SelectInputSplrCodeEditCell = (params) => {
    const [formData, setFormData] = useState([{}])
    useEffect(() => {
        getOptions();
    }, []);
    const getOptions = async () => {
        const parmas = {
            "type": "supplier"
        }
        const {
            data: { code, data, msg },
        } = await request.post("companys/page", parmas);
        if (code === 200) {
            setFormData(data.records)
        } else {
            notify(msg);
        }
    }
    return (
        <Select
            value={params.value}
            onChange={(e) => {
                params.api.setEditCellValue({
                    id: params.id,
                    field: params.field,
                    value: e.target.value,
                })
                const selectedSupplier = formData.find(supplier => supplier.id === e.target.value);
                // 如果找到对应的供应商记录,同时更新splrCode字段
                if (selectedSupplier) {
                    params.api.setEditCellValue({
                        id: params.id,
                        field: 'splrName',
                        value: selectedSupplier.name,
                    });
                }
            }}
            fullWidth
        >
            {formData.map(e => {
                return (
                    <MenuItem value={e.id} children={e.name} key={e.id} />
                );
            })}
        </Select>
    );
};
const TransferTableView = ({ tabelData, setTableData, orderId, selectedRows, setSelectedRows, tableRef }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [extendColumns, setExtendColumns] = useState([]);
    const [columns, setColumns] = useState([
        {
            field: 'maktx',
            headerName: translate('table.field.outStockItem.maktx'),
            width: 250,
            editable: false,
        },
        {
            field: 'matnrCode',
            headerName: translate('table.field.outStockItem.matnrCode'),
            width: 130,
            editable: false,
        },
        {
            field: 'anfme',
            headerName: translate('table.field.outStockItem.anfme') + "*",
            type: 'number',
            minWidth: 100,
            flex: 1,
            editable: true,
            valueFormatter: (val) => val < 0 ? 0 : val,
            headerClassName: "custom",
        },
        {
            field: 'splrCode',
            headerName: translate('table.field.outStockItem.splrCode') + "*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrCodeEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        {
            field: 'splrName',
            headerName: translate('table.field.outStockItem.splrName') + "*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrNameEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        {
            field: 'splrBatch',
            headerName: translate('table.field.outStockItem.splrBatch'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
        {
            field: 'poCode',
            headerName: translate('table.field.outStockItem.poDetlCode'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
        {
            field: 'stockUnit',
            headerName: translate('table.field.outStockItem.stockUnit'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
    ])
    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(() => {
        if (extendColumns == undefined || extendColumns.length < 1) {
            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])
            setExtendColumns(cols);
        } else {
            notify(msg);
        }
    }
    const handleDelete = (row) => {
        const newData = _.filter(cdata.current, (item) => item.matnrId !== row.matnrId);
        setTableData(newData);
    };
    const processRowUpdate = (newRow, oldRow) => {
        const rows = tabelData.map((r) =>
            r.matnrId === newRow.matnrId ? { ...newRow } : r
        )
        setTableData(rows)
        return newRow;
    };
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    tableRef.current = useGridApiRef();
    return (
        <Box>
            <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={[15, 25, 50, 100]}
                editMode="row"
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                sx={{
                    height: 610,
                    '& .MuiDataGrid-cell input': {
                        border: '1px solid #ccc'
                    },
                }}
            />
            {/* <Box sx={{
                padding: 2,
                position: 'absolute',
                right: 1
            }}>
                <Button variant="contained" onClick={() => handleSubmit()} startIcon={<SaveIcon/>} >
                    {translate('toolbar.confirm')}
                </Button>
            </Box> */}
        </Box>
    );
};
export default ManualCreate;
rsf-admin/src/page/orders/transfer/TransferCreate.jsx
@@ -23,19 +23,30 @@
    DialogActions,
    DialogContent,
    DialogTitle,
    TextField,
    Button,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton.jsx";
import StatusSelectInput from "../../components/StatusSelectInput.jsx";
import MemoInput from "../../components/MemoInput.jsx";
import CreateBySelectMats from "./CreateBySelectMats.jsx";
import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
import ConfirmButton from "../../components/ConfirmButton";
import DictSelect from "../../components/DictSelect";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
const TransferCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const { open, setOpen, orderId } = props;
    const tableRef = useRef();
    const notify = useNotify();
    const translate = useTranslate();
    const [tabelData, setTableData] = useState([]);
    const [disabled, setDisabled] = useState(false)
    const [selectedRows, setSelectedRows] = useState([]);
    const [createDialog, setCreateDialog] = useState(false);
    const [formData, setFormData] = useState({});
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
@@ -43,10 +54,50 @@
        }
    };
    const handleSubmit = async () => {
        setFinally()
        setDisabled(true)
        if (asnId === 0) {
            const parmas = {
                "orders": formData,
                "items": tabelData,
            }
            const res = await request.post(`/outStock/items/save`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                resetData()
            } else {
                notify(res.data.msg);
            }
        } else {
            const parmas = {
                "orders": formData,
                "items": tabelData,
            }
            const res = await request.post(`/outStock/items/update`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                resetData()
            } else {
                notify(res.data.msg);
            }
        }
        setDisabled(false)
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleDeleteItem = () => {
        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
        setTableData(newTableData);
    }
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
@@ -54,146 +105,280 @@
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                aria-hidden
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                maxWidth="xl"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                }}>
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.transfer.code"
                                        source="code"
                                        parse={v => v}
                                        autoFocus
                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                        <Form defaultValues={formData}>
                            <Grid container spacing={2}>
                                <Grid item md={2}>
                                    <DictSelect
                                        label={translate("table.field.outStock.wkType")}
                                        value={formData.wkType}
                                        variant="filled"
                                        group='2'
                                        onChange={(e) => handleChange(e.target.value, 'wkType')}
                                        dictTypeCode="sys_business_type"
                                        required
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.transfer.type"
                                        source="type"
                                <Grid item md={2}>
                                    <TextField
                                        label={translate("table.field.outStock.poCode")}
                                        value={formData.poCode}
                                        variant="filled"
                                        size='small'
                                        onChange={(e) => handleChange(e.target.value, 'poCode')}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.transfer.source"
                                        source="source"
                                        choices={[
                                            { id: 1, name: 'ERP系统' },
                                            { id:  2, name: 'WMS系统生成' },
                                            { id:  3, name: 'EXCEL导入 ' },
                                            { id:  4, name: 'QMS系统' },
                                        ]}
                                <Grid item md={2}>
                                    <TextField
                                        label={translate("table.field.outStock.logisNo")}
                                        value={formData.logisNo}
                                        variant="filled"
                                        size='small'
                                        onChange={(e) => handleChange(e.target.value, 'logisNo')}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.transfer.exceStatus"
                                        source="exceStatus"
                                        choices={[
                                            { id: 0, name: '未执行' },
                                            { id:  1, name: '执行中' },
                                            { id:  2, name: '执行完成' },
                                        ]}
                                <Grid item md={2}>
                                    <DateInput
                                        source="arrTime"
                                        label="table.field.outStock.arrTime"
                                        size='small'
                                        variant="filled"
                                        value={formData.arrTime}
                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.transfer.orgWareId"
                                        source="orgWareId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.transfer.orgWareName"
                                        source="orgWareName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.transfer.tarWareId"
                                        source="tarWareId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.transfer.tarWareName"
                                        source="tarWareName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.transfer.orgAreaId"
                                        source="orgAreaId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.transfer.orgAreaName"
                                        source="orgAreaName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.transfer.tarAreaId"
                                        source="tarAreaId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.transfer.tarAreaName"
                                        source="tarAreaName"
                                        parse={v => v}
                                    />
                                </Grid>
                        </Form>
                    </Box>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                    <Box sx={{ mt: 2 }}>
                        <Stack direction="row" spacing={2}>
                            <Button variant="contained" onClick={() => setCreateDialog(true)} >
                                {translate('common.action.newAddMats')}
                            </Button>
                            <ConfirmButton label={"toolbar.delete"} variant="outlined" color="error" onConfirm={handleDeleteItem} />
                                    </Stack>
                                </Grid>
                            </Grid>
                    </Box>
                    <Box sx={{ mt: 2 }}>
                        <TransferTableView
                            tabelData={tabelData}
                            setTableData={setTableData}
                            asnId={orderId}
                            selectedRows={selectedRows}
                            setSelectedRows={setSelectedRows}
                            tableRef={tableRef}>
                        </TransferTableView>
                    </Box>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                        <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
                            {translate('toolbar.confirm')}
                        </Button>
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
            <CreateBySelectMats
                open={createDialog}
                setOpen={setCreateDialog}
                data={tabelData}
                setData={setTableData}
            />
        </>
    )
}
const TransferTableView = ({ tabelData, setTableData, orderId, selectedRows, setSelectedRows, tableRef }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [columns, setColumns] = useState([
        {
            field: 'maktx',
            headerName: translate('table.field.outStockItem.maktx'),
            width: 250,
            editable: false,
        },
        {
            field: 'matnrCode',
            headerName: translate('table.field.outStockItem.matnrCode'),
            width: 130,
            editable: false,
        },
        {
            field: 'anfme',
            headerName: translate('table.field.outStockItem.anfme') + "*",
            type: 'number',
            minWidth: 100,
            flex: 1,
            editable: true,
            valueFormatter: (val) => val < 0 ? 0 : val,
            headerClassName: "custom",
        },
        {
            field: 'splrCode',
            headerName: translate('table.field.outStockItem.splrCode') + "*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrCodeEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        {
            field: 'splrName',
            headerName: translate('table.field.outStockItem.splrName') + "*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrNameEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        {
            field: 'splrBatch',
            headerName: translate('table.field.outStockItem.splrBatch'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
        {
            field: 'poCode',
            headerName: translate('table.field.outStockItem.poDetlCode'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
        {
            field: 'stockUnit',
            headerName: translate('table.field.outStockItem.stockUnit'),
            minWidth: 100,
            flex: 1,
            editable: true,
        },
    ])
    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.matnrId !== row.matnrId);
        setTableData(newData);
    };
    const processRowUpdate = (newRow, oldRow) => {
        const rows = tabelData.map((r) =>
            r.matnrId === newRow.matnrId ? { ...newRow } : r
        )
        setTableData(rows)
        return newRow;
    };
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    tableRef.current = useGridApiRef();
    return (
        <div style={{ height: 400, 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={[15, 25, 50, 100]}
                editMode="row"
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                sx={{
                    '& .MuiDataGrid-cell input': {
                        border: '1px solid #ccc'
                    },
                }}
            />
        </div>
    );
};
export default TransferCreate;
rsf-admin/src/page/orders/transfer/TransferItemEdit.jsx
File was deleted
rsf-admin/src/page/orders/transfer/TransferList.jsx
@@ -33,16 +33,19 @@
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import TransferCreate from "./TransferCreate.jsx";
import TransferPanel from "./TransferPanel.jsx";
import EmptyData from "../../components/EmptyData.jsx";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting.js';
import MyCreateButton from "../../components/MyCreateButton.jsx";
import MyExportButton from '../../components/MyExportButton.jsx';
import PageEditDrawer from "../../components/PageEditDrawer";
import PageDrawer from "../../components/PageDrawer.jsx";
import EmptyData from "../../components/EmptyData.jsx";
import MyField from "../../components/MyField.jsx";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting.js';
import TransferCreate from "./TransferCreate.jsx";
import { styled } from '@mui/material/styles';
import TransferPanel from "./TransferPanel.jsx";
import * as Common from '@/utils/common.js';
import ManualCreate from "./ManualCreate.jsx";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -62,7 +65,6 @@
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="code" label="table.field.transfer.code" />,
    <NumberInput source="type" label="table.field.transfer.type" />,
    <SelectInput source="source" label="table.field.transfer.source"
@@ -88,7 +90,6 @@
    <TextInput source="orgAreaName" label="table.field.transfer.orgAreaName" />,
    <NumberInput source="tarAreaId" label="table.field.transfer.tarAreaId" />,
    <TextInput source="tarAreaName" label="table.field.transfer.tarAreaName" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
@@ -103,7 +104,6 @@
const TransferList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
@@ -138,7 +138,7 @@
                    rowClick={(id, resource, record) => false}
                    expand={() => <TransferPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                    omit={['id', 'createTime', 'createBy$', 'memo']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.transfer.code" />
@@ -153,14 +153,9 @@
                    <TextField source="orgAreaName" label="table.field.transfer.orgAreaName" />
                    <NumberField source="tarAreaId" label="table.field.transfer.tarAreaId" />
                    <TextField source="tarAreaName" label="table.field.transfer.tarAreaName" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <TextField source="updateBy$" label="common.field.updateBy" />
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <TextField source="createBy$" label="common.field.createBy" />
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
@@ -170,16 +165,13 @@
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <TransferCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='Transfer Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            <PageEditDrawer
                title={"toolbar.createTransfer"}
                drawerVal={createDialog}
                setDrawerVal={setCreateDialog}
            >
            </PageDrawer>
                <ManualCreate />
            </PageEditDrawer>
        </Box>
    )
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/TransferController.java
@@ -9,10 +9,14 @@
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.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.controller.params.TransferItemParams;
import com.vincent.rsf.server.manager.entity.Transfer;
import com.vincent.rsf.server.manager.entity.excel.CheckOrderTemplate;
import com.vincent.rsf.server.manager.service.TransferService;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.controller.BaseController;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -63,6 +67,12 @@
        transfer.setCreateTime(new Date());
        transfer.setUpdateBy(getLoginUserId());
        transfer.setUpdateTime(new Date());
        if (Objects.isNull(transfer.getCode())) {
            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TRANSFER_ORDER_CODE, transfer);
            transfer.setCode(ruleCode);
        }
        if (!transferService.save(transfer)) {
            return R.error("Save Fail");
        }
@@ -105,6 +115,28 @@
        return R.ok().add(vos);
    }
    @PostMapping("/transfer/items/save")
    @ApiOperation("保存主单及明细")
    @PreAuthorize("hasAuthority('manager:transfer:save')")
    public R saveOutStock(@RequestBody TransferItemParams params) throws Exception {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        return transferService.saveTransfer(params, getLoginUserId());
    }
    @ApiOperation("单据信息修改")
    @PostMapping("/transfer/items/update")
    @PreAuthorize("hasAuthority('manager:outStock:update')")
    public R orderAndrItemUpdate(@RequestBody TransferItemParams params) throws Exception {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        return transferService.updateTransfer(params, getLoginUserId());
    }
    /**
     * @author Ryan
     * @description 下载模板
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/TransferItemParams.java
New file
@@ -0,0 +1,24 @@
package com.vincent.rsf.server.manager.controller.params;
import com.vincent.rsf.server.manager.entity.Transfer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Data
@Accessors(chain = true)
@ApiModel(value = "TransferItemParams", description = "调拔单参数")
public class TransferItemParams implements Serializable {
    @ApiModelProperty("调拔单")
    private Transfer transfer;
    @ApiModelProperty("调拔单明细")
    private List<Map<String, Object>>  items;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Transfer.java
@@ -3,6 +3,13 @@
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.server.manager.enums.OrderType;
import com.vincent.rsf.server.system.constant.DictTypeCode;
import com.vincent.rsf.server.system.entity.DictData;
import com.vincent.rsf.server.system.service.DictDataService;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -20,8 +27,10 @@
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
@Data
@Accessors(chain = true)
@TableName("man_transfer")
public class Transfer implements Serializable {
@@ -56,7 +65,7 @@
     * 执行状态: 0: 未执行   1: 执行中   2: 执行完成  
     */
    @ApiModelProperty(value= "执行状态: 0: 未执行   1: 执行中   2: 执行完成  ")
    private Integer exceStatus;
    private Short exceStatus;
    /**
     * 源仓库ID
@@ -159,7 +168,7 @@
    public Transfer() {}
    public Transfer(String code,Integer type,Integer source,Integer exceStatus,Long orgWareId,String orgWareName,Long tarWareId,String tarWareName,Long orgAreaId,String orgAreaName,Long tarAreaId,String tarAreaName,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
    public Transfer(String code,Integer type,Integer source,Short exceStatus,Long orgWareId,String orgWareName,Long tarWareId,String tarWareName,Long orgAreaId,String orgAreaName,Long tarAreaId,String tarAreaName,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.code = code;
        this.type = type;
        this.source = source;
@@ -222,18 +231,19 @@
    }
    public String getExceStatus$(){
        if (null == this.exceStatus){ return null; }
        switch (this.exceStatus){
            case 0:
                return "未执行";
            case  1:
                return "执行中";
            case  2:
                return "执行完成";
            default:
                return String.valueOf(this.exceStatus);
        if (Cools.isEmpty(this.exceStatus)){
            return "";
        }
        DictDataService dictDataService = SpringUtils.getBean(DictDataService.class);
        DictData dictData = dictDataService.getOne(new LambdaQueryWrapper<DictData>()
                .eq(DictData::getDictTypeCode, DictTypeCode.SYS_ORDER_SOURCE)
                .eq(DictData::getValue, this.exceStatus));
        if (Objects.isNull(dictData)) {
            return null;
    }
        return dictData.getLabel();
    }
    public String getStatus$(){
        if (null == this.status){ return null; }
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/OrderSourceType.java
@@ -16,10 +16,37 @@
    ;
    OrderSourceType(String val, String desc) {
        this.val = Short.parseShort(val);
        this.val = Integer.parseInt(val);
        this.desc = desc;
    }
    public Short val;
    public Integer val;
    public String desc;
    public static Integer getSourceVal(String desc) {
        if (desc.equals(OrderSourceType.ORDER_SOURCE_TYPE_ERP.desc)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_ERP.val;
        } else if (desc.equals(OrderSourceType.ORDER_SOURCE_TYPE_SYSTEM.desc)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_SYSTEM.val;
        } else if (desc.equals(OrderSourceType.ORDER_SOURCE_TYPE_EXCEL.desc)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_EXCEL.val;
        } else if (desc.equals(OrderSourceType.ORDER_SOURCE_TYPE_QMS.desc)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_QMS.val;
        }
        return null;
    }
    public static String getSourceDesc(Integer val) {
        if (val.equals(OrderSourceType.ORDER_SOURCE_TYPE_ERP.val)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_ERP.desc;
        } else if (val.equals(OrderSourceType.ORDER_SOURCE_TYPE_SYSTEM.val)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_SYSTEM.desc;
        } else if (val.equals(OrderSourceType.ORDER_SOURCE_TYPE_EXCEL.val)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_EXCEL.desc;
        } else if (val.equals(OrderSourceType.ORDER_SOURCE_TYPE_QMS.val)) {
            return OrderSourceType.ORDER_SOURCE_TYPE_QMS.desc;
        }
        return null;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/OrderType.java
@@ -12,7 +12,7 @@
    ORDER_OUT("out", "出库单"),
    ORDER_IN("in", "入库单"),
    ORDER_CHECK("check", "盘点单");
    ;
    OrderType(String type, String desc) {
        this.type = type;
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/TransferType.java
New file
@@ -0,0 +1,25 @@
package com.vincent.rsf.server.manager.enums;
/**
 * @author Ryan
 * @date 2025/7/25
 * @description: 调拔类型
 * @version 1.0
 */
public enum TransferType {
    //调拔类型
    TRANSFER_TYPE_WAREHOUSE("1" ,"跨仓调拔"),
    TRANSFER_TYPE_AREAS("2", "跨区调拔"),
    TRANSFER_TYPE_LOCAL("0", "本地调拔");
    private Integer val;
    private String desc;
    TransferType(String val, String desc) {
        this.val = Integer.parseInt(val);
        this.desc = desc;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TransferService.java
@@ -1,8 +1,14 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.controller.params.TransferItemParams;
import com.vincent.rsf.server.manager.entity.Transfer;
public interface TransferService extends IService<Transfer> {
    R saveTransfer(TransferItemParams params, Long loginUserId);
    R updateTransfer(TransferItemParams params, Long loginUserId);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/MatnrServiceImpl.java
@@ -115,15 +115,15 @@
            if (null != group){
                pageParam.getWhere().getMap().remove("groupId");
                if(!group.getCode().equals("01")){
                    List<MatnrGroup> matnrGroups = matnrGroupService.list(new LambdaQueryWrapper<MatnrGroup>().eq(MatnrGroup::getParentId, Long.parseLong(groupId.toString())).select(MatnrGroup::getId));
                    List<MatnrGroup> matnrGroups = matnrGroupService.list(new LambdaQueryWrapper<MatnrGroup>()
                            .eq(MatnrGroup::getParentId, Long.parseLong(groupId.toString()))
                            .select(MatnrGroup::getId));
                    if (!matnrGroups.isEmpty()) {
                        longs = matnrGroups.stream().map(MatnrGroup::getId).collect(Collectors.toList());
                    }
                    longs.add(group.getId());
                }
            }
        }
        QueryWrapper<Matnr> queryWrapper = pageParam.buildWrapper(true);
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TransferServiceImpl.java
@@ -1,12 +1,103 @@
package com.vincent.rsf.server.manager.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.manager.controller.params.TransferItemParams;
import com.vincent.rsf.server.manager.entity.WkOrder;
import com.vincent.rsf.server.manager.entity.WkOrderItem;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.enums.CheckExceStatus;
import com.vincent.rsf.server.manager.enums.OrderSourceType;
import com.vincent.rsf.server.manager.enums.OrderType;
import com.vincent.rsf.server.manager.mapper.TransferMapper;
import com.vincent.rsf.server.manager.entity.Transfer;
import com.vincent.rsf.server.manager.service.TransferService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
@Service("transferService")
public class TransferServiceImpl extends ServiceImpl<TransferMapper, Transfer> implements TransferService {
    /**
     * @author Ryan
     * @date 2025/7/25
     * @description: 保存调拔单及明细
     * @version 1.0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveTransfer(TransferItemParams params, Long loginUserId) {
        if (Objects.isNull(params.getTransfer())) {
            throw new CoolException("主单信息不能为空");
        }
        Transfer transfer = params.getTransfer();
        if (StringUtils.isBlank(transfer.getType() + "")) {
            throw new CoolException("业务类型不能为空!!");
        }
        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TRANSFER_ORDER_CODE, transfer);
        if (StringUtils.isBlank(ruleCode)) {
            throw new CoolException("编码规则错误:请检查「SYS_TRANSFER_ORDER_CODE」是否设置正确!!");
        }
        transfer.setCode(ruleCode)
                .setExceStatus(CheckExceStatus.CHECK_ORDER_STATUS_UN_EXCE.val)
                .setSource(OrderSourceType.ORDER_SOURCE_TYPE_SYSTEM.val)
                .setUpdateBy(loginUserId)
                .setCreateBy(loginUserId);
        if (!this.save(transfer)) {
            throw new CoolException("主单保存失败!!");
        }
        if (params.getItems().isEmpty()) {
            throw new CoolException("收货通知单明细不能为空!!");
        }
        params.setTransfer(transfer);
        try {
            svaeOrUpdateOrderItem(params, loginUserId);
        } catch (Exception e) {
            throw new CoolException(e.getMessage());
        }
        return R.ok();
    }
    private void svaeOrUpdateOrderItem(TransferItemParams params, Long loginUserId) {
//        Transfer orders = params.getTransfer();
//        params.getItems().forEach(item -> {
//            item.put("orderId", orders.getId());
//            item.put("orderCode", orders.getCode());
//            item.put("poCode", orders.getPoCode());
//            item.put("createBy", loginUserId);
//            item.put("updateBy", loginUserId);
//            if (!asnOrderItemService.fieldsSave(item, loginUserId)) {
//                throw new CoolException("明细保存失败!!");
//            }
//        });
//        List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
//                .eq(WkOrderItem::getOrderId, params.getOrders().getId()));
//        Double sum = orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum();
//        orders.setAnfme(sum);
//        if (!this.updateById(orders)) {
//            throw new CoolException("计划收货数量修改失败!!");
//        }
    }
    /**
     * @author Ryan
     * @date 2025/7/25
     * @description: 调拔单及明细修改
     * @version 1.0
     */
    @Override
    public R updateTransfer(TransferItemParams params, Long loginUserId) {
        return null;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java
@@ -91,4 +91,7 @@
    public final static String SYS_CHECK_DIFF_REASON =  "sys_check_diff_reason";
    /**盘点类型*/
    public final static String SYS_CHECK_TYPE = "sys_check_type";
    /**单据来源*/
    public final static String SYS_ORDER_SOURCE = "sys_order_source";
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
@@ -88,5 +88,8 @@
    /**盘点单号*/
    public final static String SYS_CHECK_RULE_CODE = "sys_check_rule_code";
    /**调拔单编码规则*/
    public final static String SYS_TRANSFER_ORDER_CODE = "sys_transfer_order_code";
}