From c3bd7262ff89c7594ec368f76ea910e6212769af Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期二, 29 四月 2025 13:53:03 +0800
Subject: [PATCH] 1. 出库单新增修改优化

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java          |   49 +
 rsf-admin/src/page/orders/outStock/AsnWareModal.jsx                                                    |  249 ++++++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java                   |    4 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java              |   40 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java                   |    4 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java             |   25 
 rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/severlet/CustomParameterFilter.java |    2 
 rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx                                                  |   83 -
 rsf-admin/src/page/orders/outStock/SelectMatnrModal.jsx                                                |  613 +++++++++++++++++++++
 rsf-admin/src/page/components/DictSelect.jsx                                                           |    1 
 rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx                                               |    1 
 rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx                                                     |    7 
 rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx                                                   |  643 ++++++++++++++++++++++
 rsf-admin/src/i18n/zh.js                                                                               |    1 
 rsf-admin/src/page/orders/outStock/OutOrderList.jsx                                                    |   15 
 rsf-admin/src/i18n/en.js                                                                               |    1 
 16 files changed, 1,656 insertions(+), 82 deletions(-)

diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index 37cd4e3..596f515 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -56,6 +56,7 @@
             expand: 'Expand',
             expandAll: 'Expand All',
             collapse: 'Collapse',
+            newAddMats: 'New Mats',
             collapseAll: 'Collapse All',
             scope: 'Assign',
             import: {
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 8f6d2d3..0c681ce 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -56,6 +56,7 @@
             expand: '灞曞紑',
             expandAll: '鍏ㄩ儴灞曞紑',
             collapse: '鎶樺彔',
+            newAddMats: '鏂板鐗╂枡',
             collapseAll: '鍏ㄩ儴鎶樺彔',
             scope: '鏉冮檺',
             inputPlaceholder: '绔欑偣鍙~澶氫釜锛屼腑闂翠互鑻辨枃閫楀彿鍖哄垎锛�,锛�',
diff --git a/rsf-admin/src/page/components/DictSelect.jsx b/rsf-admin/src/page/components/DictSelect.jsx
index 72c9d76..d42a8ee 100644
--- a/rsf-admin/src/page/components/DictSelect.jsx
+++ b/rsf-admin/src/page/components/DictSelect.jsx
@@ -44,6 +44,7 @@
             <Select
                 labelId="demo-select-small-label"
                 value={validValue}
+                variant="filled"
                 onChange={handleChange}
                 size='small'
             >
diff --git a/rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx b/rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx
new file mode 100644
index 0000000..6c2dc38
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx
@@ -0,0 +1,643 @@
+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,
+    useListContext,
+    useRefresh,
+} 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
+
+
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+import ConfirmButton from "../../components/ConfirmButton";
+import AsnWareModal from "./AsnWareModal";
+import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
+import SaveIcon from '@mui/icons-material/Save';
+import request from '@/utils/request';
+import { Add, Edit, Delete } from '@mui/icons-material';
+import _, { set } from 'lodash';
+import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
+import DictionarySelect from "../../components/DictionarySelect";
+import DictSelect from "../../components/DictSelect";
+import "./asnOrder.css";
+
+const AsnOrderModal = (props) => {
+    const { open, setOpen, asnId, billReload } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const [disabled, setDisabled] = useState(false)
+
+    const [createDialog, setCreateDialog] = useState(false);
+
+    const tableRef = useRef();
+
+    useEffect(() => {
+        if (open && asnId !== 0) {
+            requestGetHead()
+            requestGetBody()
+        }
+        setDisabled(false)
+    }, [open])
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+            refresh();
+            setFormData({ type: '', wkType: '' })
+            setTableData([])
+        }
+    };
+
+    const [formData, setFormData] = useState({
+        type: '',
+        wkType: '',
+        poCode: '',
+        logisNo: '',
+        arrTime: ''
+    });
+
+    const [tabelData, setTableData] = useState([]);
+
+
+    const handleChange = (value, name) => {
+        setFormData((prevData) => ({
+            ...prevData,
+            [name]: value
+        }));
+        console.log(formData);
+    };
+
+    const resetData = () => {
+        setFormData({
+            type: '',
+            wkType: '',
+            poCode: '',
+            logisNo: '',
+            arrTime: ''
+        })
+        setTableData([])
+    }
+
+    const setFinally = () => {
+        const rows = tableRef.current.state.editRows;
+        for (const key in rows) {
+            const find = tabelData.find(item => item.matnrId === +key);
+            find.anfme = rows[key].anfme.value;
+        }
+        setTableData([...tabelData]);
+    }
+
+    const handleSubmit = async () => {
+        setFinally()
+        setDisabled(true)
+
+        if (asnId === 0) {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+
+            const res = await request.post(`/asnOrder/items/save`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                billReload?.current()
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        } else {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+            const res = await request.post(`/asnOrder/items/update`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                billReload?.current()
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        }
+        setDisabled(false)
+
+    };
+
+
+    const handleDelete = async () => {
+        const res = await request.post(`/asnOrder/remove/${asnId}`);
+        if (res?.data?.code === 200) {
+            setOpen(false);
+            refresh();
+        } else {
+            notify(res.data.msg);
+        }
+    };
+
+    const requestGetHead = async () => {
+        const res = await request.get(`/asnOrder/${asnId}`);
+        if (res?.data?.code === 200) {
+            setFormData(res.data.data)
+        } else {
+            notify(res.data.msg);
+        }       
+    }
+
+    const requestGetBody = async () => {
+        const res = await request.post(`/asnOrderItem/page`, { asnId });
+        if (res?.data?.code === 200) {
+            setTableData(res.data.data.records)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const [selectedRows, setSelectedRows] = useState([]);
+
+
+
+    const handleDeleteItem = () => {
+        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
+        setTableData(newTableData);
+    }
+
+    return (
+        <>       
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                aria-labelledby="form-dialog-title"
+                aria-hidden
+                fullWidth
+                disableRestoreFocus
+                maxWidth="lg"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+            >                
+                <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 }}>
+                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
+                        <Form defaultValues={formData}>
+                            <Grid container spacing={2}>
+                                <Grid item md={3}>
+                                    <DictSelect
+                                        label={translate("table.field.asnOrder.type")}
+                                        value={formData.type}
+                                        onChange={(e) => handleChange(e.target.value, 'type')}
+                                        dictTypeCode="sys_order_type"
+                                        required
+                                    />
+                                </Grid>
+                                <Grid item md={3}>
+                                    <DictSelect
+                                        label={translate("table.field.asnOrder.wkType")}
+                                        value={formData.wkType}
+                                        onChange={(e) => handleChange(e.target.value, 'wkType')}
+                                        dictTypeCode="sys_business_type"
+                                        required
+                                    />
+                                </Grid>
+                                <Grid item md={3}>
+                                    <TextField
+                                        label={translate("table.field.asnOrder.poCode")}
+                                        value={formData.poCode}
+                                        onChange={(e) => handleChange(e.target.value, 'poCode')}
+                                    />
+                                </Grid>
+                                <Grid item md={3}>
+                                    <TextField
+                                        label={translate("table.field.asnOrder.logisNo")}
+                                        value={formData.logisNo}
+                                        onChange={(e) => handleChange(e.target.value, 'logisNo')}
+                                    />
+                                </Grid>
+
+                                <Grid item md={3}>
+                                    {/* <TextField
+                                        label={translate("table.field.asnOrder.arrTime")}
+                                        value={formData.arrTime}
+                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
+                                    /> */}
+                                    <DateInput
+                                        source="arrTime"
+                                        label="table.field.asnOrder.arrTime"
+                                        value={formData.arrTime}
+                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
+                                    />
+
+                                </Grid>
+                            </Grid>
+                        </Form>
+                    </Box>
+
+                    <Box sx={{ mt: 2 }}>
+                        <Stack direction="row" spacing={2}>
+                            <Button variant="contained" onClick={() => setCreateDialog(true)}>鏂板鐗╂枡</Button>
+                            {/* {asnId !== '' && <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDelete} />} */}
+                            <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDeleteItem} />
+                        </Stack>
+                    </Box>
+                    <Box sx={{ mt: 2 }}>
+                        <AsnOrderModalTable tabelData={tabelData} setTableData={setTableData} asnId={asnId} selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></AsnOrderModalTable>
+                    </Box>
+                </DialogContent>
+                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                    <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                        <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
+                            {translate('toolbar.confirm')}
+                        </Button>
+                    </Toolbar>
+                </DialogActions>
+
+            </Dialog>
+
+            <AsnWareModal
+                open={createDialog}
+                setOpen={setCreateDialog}
+                data={tabelData}
+                setData={setTableData}
+            />
+        </>
+    )
+}
+
+export default AsnOrderModal;
+
+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)
+            console.log(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);
+          
+          // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+          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)
+            console.log(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);
+              
+              // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+              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 AsnOrderModalTable = ({ tabelData, setTableData, asnId, selectedRows, setSelectedRows, tableRef }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+
+        {
+            field: 'maktx',
+            headerName: translate('table.field.asnOrderItem.maktx'),
+            width: 250,
+            editable: false,
+        },
+        {
+            field: 'matnrCode',
+            headerName: translate('table.field.asnOrderItem.matnrCode'),
+            width: 130,
+            editable: false,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.asnOrderItem.anfme')+"*",
+            type: 'number',
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            valueFormatter: (val) => val < 0 ? 0 : val,
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrCode',
+            headerName: translate('table.field.asnOrderItem.splrCode')+"*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrCodeEditCell {...params} />
+            ),
+            headerClassName: "custom",         
+        },
+        {
+            field: 'splrName',
+            headerName: translate('table.field.asnOrderItem.splrName')+"*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrNameEditCell {...params} />
+            ),
+            headerClassName: "custom",
+        },
+        // {
+        //     field: 'packName',
+        //     headerName: translate('table.field.asnOrderItem.packName'),
+        //     minWidth: 100,
+        //     flex: 1,
+        //     editable: true,
+        // },
+        // {
+        //     field: 'poDetlId',
+        //     headerName: translate('table.field.asnOrderItem.poDetlId'),
+        //     minWidth: 100,
+        //     flex: 1,
+        // },
+        {
+            field: 'poCode',
+            headerName: translate('table.field.asnOrderItem.poDetlCode')+"*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            headerClassName: "custom",
+        },
+
+        {
+            field: 'stockUnit',
+            headerName: translate('table.field.asnOrderItem.stockUnit'),
+            minWidth: 100,
+            flex: 1,
+            editable: false,
+        },
+        // {
+        //     field: 'purQty',
+        //     headerName: translate('table.field.asnOrderItem.purQty'),
+        //     minWidth: 100,
+        //     flex: 1,
+        //     editable: true,
+        // },
+        {
+            field: 'purUnit',
+            headerName: translate('table.field.asnOrderItem.purUnit'),
+            minWidth: 100,
+            flex: 1,
+            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.matnrId !== row.matnrId);
+        setTableData(newData);
+    };
+
+
+    const processRowUpdate = (newRow, oldRow) => {
+        const rows = tabelData.map((r) =>
+            r.matnrId === newRow.matnrId ? { ...newRow } : r
+        )
+        setTableData(rows)
+        // setTableData((prevData) =>
+        //     prevData.map((r) =>
+        //         r.matnrId === newRow.matnrId ? { ...newRow } : r
+        //     )
+
+        // );
+
+        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}
+                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>
+    );
+};
+
diff --git a/rsf-admin/src/page/orders/outStock/AsnWareModal.jsx b/rsf-admin/src/page/orders/outStock/AsnWareModal.jsx
new file mode 100644
index 0000000..8f3788b
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/AsnWareModal.jsx
@@ -0,0 +1,249 @@
+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 AsnWareModal = (props) => {
+    const { open, setOpen, data, setData } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+    const refresh = useRefresh();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    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({
+            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: 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
+            }}>
+                閫夋嫨鐗╂枡
+                <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={4}>
+                            <TextField
+                                label={translate('table.field.matnr.name')}
+                                name="name"
+                                value={formData.name}
+                                onChange={handleChange}
+                                size="small"
+                            />
+                        </Grid>
+                        <Grid item md={4}>
+                            <TextField
+                                label={translate('table.field.matnr.code')}
+                                name="code"
+                                value={formData.code}
+                                onChange={handleChange}
+                                size="small"
+                            />
+                        </Grid>
+                        <Grid item md={4}>
+                            <TreeSelectInput
+                                label="table.field.matnr.groupId"
+                                value={formData.groupId}
+                                resource={'matnrGroup'}
+                                source="groupId"
+                                name="groupId"
+                                onChange={handleChange}
+                            />
+                        </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 AsnWareModal;
+
+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: '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(() => {
+        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>
+    );
+};
\ No newline at end of file
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx b/rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx
index 25a8314..40fba47 100644
--- a/rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx
+++ b/rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx
@@ -27,6 +27,8 @@
     useList,
     ListContextProvider,
     useListContext,
+    Button,
+    useRecordContext,
 } from 'react-admin';
 import {
     Dialog,
@@ -43,7 +45,8 @@
 import StatusSelectInput from "../../components/StatusSelectInput";
 import OutOrderItemList from "./OutOrderItemList";
 import MemoInput from "../../components/MemoInput";
-
+import AddIcon from '@mui/icons-material/Add';
+import SelectMatnrModal from "./SelectMatnrModal";
 
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
     '& .css-1vooibu-MuiSvgIcon-root': {
@@ -67,6 +70,7 @@
     const translate = useTranslate();
     const notify = useNotify();
     const [drawerVal, setDrawerVal] = useState(false);
+    const [matCreate, setMatCreate] = useState(false);
 
     const handleClose = (event, reason) => {
         if (reason !== "backdropClick") {
@@ -120,7 +124,7 @@
                         </DialogTitle>
                         <DialogContent sx={{ mt: 2 }}>
                             <>
-                                <Grid container rowSpacing={2} columnSpacing={2}>
+                                <Grid container>
                                     <Grid item xs={12} display="flex" gap={1}>
                                         <TextInput
                                             label="table.field.asnOrder.poCode"
@@ -154,12 +158,13 @@
                                             source="anfme"
                                             validate={required()}
                                         />
+
+                                    </Grid>
+                                    <Grid item xs={12} display="flex" gap={1}>
                                         <NumberInput
                                             label="table.field.asnOrder.qty"
                                             source="qty"
                                         />
-                                    </Grid>
-                                    <Grid item xs={12} display="flex" gap={1}>
                                         <TextInput
                                             label="table.field.asnOrder.logisNo"
                                             source="logisNo"
@@ -179,64 +184,15 @@
                                         />
                                     </Grid>
                                 </Grid>
+                                <SelectMatnrModal open={matCreate} setOpen={setMatCreate} />
                                 <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                                     <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                                        <AddOutOrderButton setMatCreate={setMatCreate} />
                                         <SaveButton />
                                     </Toolbar>
                                 </DialogActions>
-
                                 <Box>
-                                    <ListContextProvider
-                                        // resource="waveItem脧"
-                                        value={listContext}
-                                        sx={{
-                                            flexGrow: 1,
-                                            transition: (theme) =>
-                                                theme.transitions.create(['all'], {
-                                                    duration: theme.transitions.duration.enteringScreen,
-                                                }),
-                                            marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
-                                        }}
-                                        title={"menu.waveItem"}
-                                        empty={false}
-                                        sort={{ field: "create_time", order: "desc" }}
-                                        filters={false}
-                                        actions={(
-                                            <TopToolbar>
-                                                <SelectColumnsButton preferenceKey='waveItem' />
-                                            </TopToolbar>
-                                        )}
-                                        perPage={DEFAULT_PAGE_SIZE}
-                                    >
-                                        <StyledDatagrid
-                                            preferenceKey='waveItem'
-                                            bulkActionButtons={false}
-                                            rowClick={(id, resource, record) => false}
-                                            expand={false}
-                                            expandSingle={false}
-                                            omit={['id', 'createTime', 'matnrId', 'waveId', 'batch', 'orderItemId', 'unit', 'batch', 'trackCode', 'fieldsIndex', 'createBy', 'memo']}
-                                        >
-                                            <NumberField source="id" />
-                                            <NumberField source="waveId" label="table.field.waveItem.waveId" />
-                                            <TextField source="waveCode" label="table.field.waveItem.waveCode" />
-                                            <TextField source="orderCode" label="table.field.waveItem.orderCode" />
-                                            <NumberField source="matnrId" label="table.field.waveItem.matnrId" />
-                                            <TextField source="matnrCode" label="table.field.waveItem.matnrCode" />
-                                            <TextField source="batch" label="table.field.waveItem.batch" />
-                                            <TextField source="splrBatch" label="table.field.waveItem.splrBatch" />
-                                            <NumberField source="orderItemId" label="table.field.waveItem.orderItemId" />
-                                            <TextField source="unit" label="table.field.waveItem.unit" />
-                                            <TextField source="trackCode" label="table.field.waveItem.trackCode" />
-                                            <TextField source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />
-                                            <NumberField source="anfme" label="table.field.waveItem.anfme" />
-                                            <NumberField source="workQty" label="table.field.waveItem.workQty" />
-                                            <NumberField source="qty" label="table.field.waveItem.qty" />
-                                            <NumberField source="stockQty" label="table.field.waveItem.stockQty" />
-                                            <WrapperField cellClassName="opt" label="table.field.waveItem.stockLocs">
-                                                {/* <TagsField /> */}
-                                            </WrapperField>
-                                        </StyledDatagrid>
-                                    </ListContextProvider>
+
                                 </Box>
                             </>
                         </DialogContent>
@@ -248,3 +204,18 @@
 }
 
 export default OutOrderCreate;
+
+
+const AddOutOrderButton = (setMatCreate) => {
+    const record = useRecordContext();
+    const addMats = (event) => {
+        event.stopPropagation();
+        setMatCreate(true)
+    }
+
+    return (
+        <Button label={"common.action.newAddMats"} onClick={addMats} variant="contained" sx={{ padding: '0.6em', marginRight: '1em' }}>
+            <AddIcon />
+        </Button>
+    );
+}
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
index 23ed27e..2b181eb 100644
--- a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
+++ b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -56,6 +56,7 @@
 import AddIcon from '@mui/icons-material/Add';
 import OutOrderModal from "./OutOrderModal";
 import PublicIcon from '@mui/icons-material/Public';
+import SelectMatnrModal from "./SelectMatnrModal";
 
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
   '& .css-1vooibu-MuiSvgIcon-root': {
@@ -139,7 +140,7 @@
           <TopToolbar>
             <FilterButton />
             <CreateByOrderButton setCreateDialog={setCreateDialog} />
-            <MyCreateButton onClick={() => { setManualDialog(true) }} />
+            <MyCreateButton onClick={() => { setManualDialog(true); setmodalType(0) }} />
             <SelectColumnsButton preferenceKey='outStock' />
             <ImportButton value={'asnOrderItem'} />
             {/* <MyExportButton /> */}
@@ -173,12 +174,19 @@
           <BillStatusField cellClassName="status" source="exceStatus" label="table.field.outStock.exceStatus" />
           <TextField source="memo" label="common.field.memo" sortable={false} />
           <WrapperField cellClassName="opt" label="common.field.opt" >
+            <MyButton setCreateDialog={setManualDialog} setmodalType={setmodalType} />
             <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
             <CancelButton />
           </WrapperField>
         </StyledDatagrid>
       </List>
-      <OutOrderCreate
+      {/* <OutOrderCreate
+        open={manualDialog}
+        setOpen={setManualDialog}
+      /> */}
+      <SelectMatnrModal
+        asnId={modalType}
+        billReload={billReload}
         open={manualDialog}
         setOpen={setManualDialog}
       />
@@ -190,8 +198,7 @@
         title='AsnOrder Detail'
         drawerVal={drawerVal}
         setDrawerVal={setDrawerVal}
-      >
-      </PageDrawer>
+      />
     </Box >
   )
 }
diff --git a/rsf-admin/src/page/orders/outStock/SelectMatnrModal.jsx b/rsf-admin/src/page/orders/outStock/SelectMatnrModal.jsx
new file mode 100644
index 0000000..7e0652b
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/SelectMatnrModal.jsx
@@ -0,0 +1,613 @@
+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,
+    useListContext,
+    useRefresh,
+} 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
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+import ConfirmButton from "../../components/ConfirmButton";
+import AsnWareModal from "./AsnWareModal";
+import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
+import SaveIcon from '@mui/icons-material/Save';
+import request from '@/utils/request';
+import { Add, Edit, Delete } from '@mui/icons-material';
+import _, { set } from 'lodash';
+import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
+import DictionarySelect from "../../components/DictionarySelect";
+import DictSelect from "../../components/DictSelect";
+import "./asnOrder.css";
+
+const SelectMatnrModal = (props) => {
+    const { open, setOpen, asnId, billReload } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const [disabled, setDisabled] = useState(false)
+
+    const [createDialog, setCreateDialog] = useState(false);
+
+    const tableRef = useRef();
+
+    useEffect(() => {
+        if (open && asnId !== 0) {
+            requestGetHead()
+            requestGetBody()
+        }
+        setDisabled(false)
+    }, [open])
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+            refresh();
+            setFormData({ type: '', wkType: '' })
+            setTableData([])
+        }
+    };
+
+    const [formData, setFormData] = useState({
+        type: '',
+        wkType: '',
+        poCode: '',
+        logisNo: '',
+        arrTime: ''
+    });
+
+    const [tabelData, setTableData] = useState([]);
+
+    const handleChange = (value, name) => {
+        setFormData((prevData) => ({
+            ...prevData,
+            [name]: value
+        }));
+        console.log(formData);
+    };
+
+    const resetData = () => {
+        setFormData({
+            type: '',
+            wkType: '',
+            poCode: '',
+            logisNo: '',
+            arrTime: ''
+        })
+        setTableData([])
+    }
+
+    const setFinally = () => {
+        const rows = tableRef.current.state.editRows;
+        for (const key in rows) {
+            const find = tabelData.find(item => item.matnrId === +key);
+            find.anfme = rows[key].anfme.value;
+        }
+        setTableData([...tabelData]);
+    }
+
+    const handleSubmit = async () => {
+        setFinally()
+        setDisabled(true)
+
+        if (asnId === 0) {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+
+            const res = await request.post(`/asnOrder/items/save`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                billReload?.current()
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        } else {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+            const res = await request.post(`/asnOrder/items/update`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                billReload?.current()
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        }
+        setDisabled(false)
+
+    };
+
+
+    const handleDelete = async () => {
+        const res = await request.post(`/asnOrder/remove/${asnId}`);
+        if (res?.data?.code === 200) {
+            setOpen(false);
+            refresh();
+        } else {
+            notify(res.data.msg);
+        }
+    };
+
+    const requestGetHead = async () => {
+        const res = await request.get(`/asnOrder/${asnId}`);
+        if (res?.data?.code === 200) {
+            setFormData(res.data.data)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const requestGetBody = async () => {
+        const res = await request.post(`/asnOrderItem/page`, { asnId });
+        if (res?.data?.code === 200) {
+            setTableData(res.data.data.records)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const [selectedRows, setSelectedRows] = useState([]);
+
+    const handleDeleteItem = () => {
+        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
+        setTableData(newTableData);
+    }
+
+    return (
+        <>
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                aria-labelledby="form-dialog-title"
+                aria-hidden
+                fullWidth
+                disableRestoreFocus
+                maxWidth="lg"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+            >
+                <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 }}>
+                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
+                        <Form defaultValues={formData}>
+                            <Grid container spacing={2}>
+                                {/* <Grid item md={2}>
+                                    <DictSelect
+                                        label={translate("table.field.asnOrder.type")}
+                                        value={formData.type}
+                                        onChange={(e) => handleChange(e.target.value, 'type')}
+                                        dictTypeCode="sys_order_type"
+                                        required
+                                    />
+                                </Grid> */}
+                                <Grid item md={2}>
+                                    <DictSelect
+                                        label={translate("table.field.asnOrder.wkType")}
+                                        value={formData.wkType}
+                                        variant="filled"
+                                        onChange={(e) => handleChange(e.target.value, 'wkType')}
+                                        dictTypeCode="sys_business_type"
+                                        required
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <TextField
+                                        label={translate("table.field.asnOrder.poCode")}
+                                        value={formData.poCode}
+                                        variant="filled"
+                                        size='small'
+                                        onChange={(e) => handleChange(e.target.value, 'poCode')}
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <TextField
+                                        label={translate("table.field.asnOrder.logisNo")}
+                                        value={formData.logisNo}
+                                        variant="filled"
+                                        size='small'
+                                        onChange={(e) => handleChange(e.target.value, 'logisNo')}
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <DateInput
+                                        source="arrTime"
+                                        label="table.field.asnOrder.arrTime"
+                                        size='small'
+                                        variant="filled"
+                                        value={formData.arrTime}
+                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
+                                    />
+                                </Grid>
+                            </Grid>
+                        </Form>
+                    </Box>
+
+                    <Box sx={{ mt: 2 }}>
+                        <Stack direction="row" spacing={2}>
+                            <Button variant="contained" onClick={() => setCreateDialog(true)}>鏂板鐗╂枡</Button>
+                            {/* {asnId !== '' && <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDelete} />} */}
+                            <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDeleteItem} />
+                        </Stack>
+                    </Box>
+                    <Box sx={{ mt: 2 }}>
+                        <AsnOrderModalTable tabelData={tabelData} setTableData={setTableData} asnId={asnId} selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></AsnOrderModalTable>
+                    </Box>
+                </DialogContent>
+                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                    <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                        <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
+                            {translate('toolbar.confirm')}
+                        </Button>
+                    </Toolbar>
+                </DialogActions>
+            </Dialog>
+            <AsnWareModal
+                open={createDialog}
+                setOpen={setCreateDialog}
+                data={tabelData}
+                setData={setTableData}
+            />
+        </>
+    )
+}
+
+export default SelectMatnrModal;
+
+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)
+            console.log(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);
+
+                // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+                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)
+            console.log(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);
+
+                // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+                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 AsnOrderModalTable = ({ tabelData, setTableData, asnId, selectedRows, setSelectedRows, tableRef }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+        {
+            field: 'maktx',
+            headerName: translate('table.field.asnOrderItem.maktx'),
+            width: 250,
+            editable: false,
+        },
+        {
+            field: 'matnrCode',
+            headerName: translate('table.field.asnOrderItem.matnrCode'),
+            width: 130,
+            editable: false,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.asnOrderItem.anfme') + "*",
+            type: 'number',
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            valueFormatter: (val) => val < 0 ? 0 : val,
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrCode',
+            headerName: translate('table.field.asnOrderItem.splrCode') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrCodeEditCell {...params} />
+            ),
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrName',
+            headerName: translate('table.field.asnOrderItem.splrName') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrNameEditCell {...params} />
+            ),
+            headerClassName: "custom",
+        },
+        // {
+        //     field: 'packName',
+        //     headerName: translate('table.field.asnOrderItem.packName'),
+        //     minWidth: 100,
+        //     flex: 1,
+        //     editable: true,
+        // },
+        // {
+        //     field: 'poDetlId',
+        //     headerName: translate('table.field.asnOrderItem.poDetlId'),
+        //     minWidth: 100,
+        //     flex: 1,
+        // },
+        {
+            field: 'poCode',
+            headerName: translate('table.field.asnOrderItem.poDetlCode') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            headerClassName: "custom",
+        },
+
+        {
+            field: 'stockUnit',
+            headerName: translate('table.field.asnOrderItem.stockUnit'),
+            minWidth: 100,
+            flex: 1,
+            editable: false,
+        },
+        // {
+        //     field: 'purQty',
+        //     headerName: translate('table.field.asnOrderItem.purQty'),
+        //     minWidth: 100,
+        //     flex: 1,
+        //     editable: true,
+        // },
+        {
+            field: 'purUnit',
+            headerName: translate('table.field.asnOrderItem.purUnit'),
+            minWidth: 100,
+            flex: 1,
+            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.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}
+                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>
+    );
+};
+
diff --git a/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx b/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx
index 2f81a39..d37edc4 100644
--- a/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx
+++ b/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx
@@ -150,7 +150,7 @@
                 </DialogContent>
                 <DialogActions>
                     <Toolbar sx={{ width: '100%', justifyContent: 'end' }} >
-                        <GenerateTaskButton record={[record?.id]} dataSource={data} />
+                        <GenerateTaskButton record={record?.id} dataSource={data} />
                     </Toolbar>
                 </DialogActions>
             </Dialog>
@@ -165,7 +165,10 @@
     const notify = useNotify();
     const redirect = useRedirect();
     const generateTask = async () => {
-        const res = await request.post(`/wave/public/task`, { wave: record, waveItem: dataSource });
+        const params = {wave: record, waveItem: dataSource}
+        console.log('---------->');
+        console.log(record);
+        const res = await request.post(`/wave/public/task`, { wave: record?.record, waveItem: record?.dataSource });
         if (res?.data?.code === 200) {
             notify(res.data.msg);
             redirect("/task")
diff --git a/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx b/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
index 86a989a..cf5d60c 100644
--- a/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
+++ b/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
@@ -116,7 +116,6 @@
     const translate = useTranslate();
     const [createDialog, setCreateDialog] = useState(false);
     const [drawerVal, setDrawerVal] = useState(false);
-    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
 
     return (
         <Box display="flex">
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/severlet/CustomParameterFilter.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/severlet/CustomParameterFilter.java
index 56b1756..ddef2aa 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/severlet/CustomParameterFilter.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/severlet/CustomParameterFilter.java
@@ -8,7 +8,7 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
-@Component
+//@Component
 public class CustomParameterFilter extends OncePerRequestFilter {
 
     @Override
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
index 2efff1c..058422e 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -13,6 +13,7 @@
 import com.vincent.rsf.server.common.domain.KeyValVo;
 import com.vincent.rsf.server.common.domain.PageParam;
 import com.vincent.rsf.server.common.utils.ExcelUtil;
+import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
 import com.vincent.rsf.server.manager.entity.AsnOrder;
 import com.vincent.rsf.server.manager.entity.AsnOrderItem;
 import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
@@ -214,4 +215,28 @@
         List<Long> ids = (List<Long>) params.get("ids");
         return outStockService.generateWaves(ids);
     }
+
+    @PostMapping("/outStock/items/save")
+    @ApiOperation("淇濆瓨涓诲崟鍙婃槑缁�")
+    @PreAuthorize("hasAuthority('manager:outStock:save')")
+    public R orderAndItem(@RequestBody AsnOrderAndItemsParams params) throws Exception {
+        if (Objects.isNull(params)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.saveOrderAndItems(params, getLoginUserId());
+    }
+
+    @ApiOperation("鍗曟嵁淇℃伅淇敼")
+    @PostMapping("/outStock/items/update")
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    public R orderAndrItemUpdate(@RequestBody AsnOrderAndItemsParams params) throws Exception {
+        if (Objects.isNull(params)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.updateOrderItem(params, getLoginUserId());
+    }
+
+
+
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
index aa19574..919b81a 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
@@ -126,4 +126,8 @@
         }
 
     }
+
+
+
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
index 22fcc64..71a70bd 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -16,4 +16,8 @@
     R genOutStock(List<Long> ids);
 
     R generateWaves(List<Long> ids);
+
+    R saveOrderAndItems(AsnOrderAndItemsParams params, Long loginUserId);
+
+    R updateOrderItem(AsnOrderAndItemsParams params, Long loginUserId);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
index ddc4991..20dc185 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -310,6 +310,55 @@
     }
 
     /**
+     * @author Ryan
+     * @description 淇濆瓨鍑哄簱涓诲崟鍙婃槑缁�
+     * @param
+     * @return
+     * @time 2025/4/29 13:47
+     */
+    @Override
+    public R saveOrderAndItems(AsnOrderAndItemsParams params, Long loginUserId) {
+        if (Objects.isNull(params.getOrders())) {
+            throw new CoolException("涓诲崟淇℃伅涓嶈兘涓虹┖");
+        }
+        AsnOrder orders = params.getOrders();
+        if (Objects.isNull(orders)) {
+            throw new CoolException("鍗曟嵁涓嶈兘涓虹┖锛侊紒");
+        }
+        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, orders);
+        if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
+            throw new CoolException("缂栫爜瑙勫垯閿欒锛氳妫�鏌ャ�孲YS_OUT_STOCK_CODE銆嶆槸鍚﹁缃纭紒锛�");
+        }
+        orders.setCode(ruleCode)
+                .setUpdateBy(loginUserId)
+                .setCreateBy(loginUserId);
+        if (!this.save(orders)) {
+            throw new CoolException("涓诲崟淇濆瓨澶辫触锛侊紒");
+        }
+        if (params.getItems().isEmpty()) {
+            throw new CoolException("鏀惰揣閫氱煡鍗曟槑缁嗕笉鑳戒负瀵掗鑺傦紒锛�");
+        }
+
+//        svaeOrUpdateOrderItem(params,loginUserId);
+
+
+
+        return null;
+    }
+
+    /**
+     * @author Ryan
+     * @description 淇敼涓诲崟鍙婃槑缁�
+     * @param
+     * @return
+     * @time 2025/4/29 13:47
+     */
+    @Override
+    public R updateOrderItem(AsnOrderAndItemsParams params, Long loginUserId) {
+        return null;
+    }
+
+    /**
      * @param
      * @param wave
      * @return
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
index f070e5f..73b2acd 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -1,6 +1,7 @@
 package com.vincent.rsf.server.manager.service.impl;
 
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -17,6 +18,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.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +28,7 @@
 import java.util.*;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service("waveService")
 public class WaveServiceImpl extends ServiceImpl<WaveMapper, Wave> implements WaveService {
 
@@ -59,39 +62,40 @@
         if (Objects.isNull(itemParams) || itemParams.isEmpty()) {
             throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
         }
-        Wave wave = (Wave) map.get("wave");
-        Wave waves = this.getById(new LambdaQueryWrapper<Wave>().in(Wave::getId, wave.getId()));
+        List<WaveItem> items = JSONArray.parseArray(JSONArray.toJSONString(itemParams), WaveItem.class);
+        String waveId = map.get("wave").toString();
+        Wave waves = this.getById(Long.parseLong(waveId));
         if (Objects.isNull(waves)) {
             throw new CoolException("娉㈡鏁版嵁涓嶅瓨鍦紒锛�");
         }
-        List<Long> list = itemParams.stream().map(WaveItem::getWaveId).collect(Collectors.toList());
-        List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().in(WaveItem::getWaveId, list));
+//        List<Long> list = itemParams.stream().map(WaveItem::getWaveId).collect(Collectors.toList());
+        List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().eq(WaveItem::getWaveId, waves.getId()));
         if (waveItems.isEmpty()) {
             throw new CoolException("娉㈡鏄庣粏涓嶅瓨鍦紒锛�");
         }
         /**鐢熸垚鍑哄簱浠诲姟*/
         try {
-            generateOutTask(itemParams, loginUserId, wave);
+            generateOutTask(items, loginUserId, waves);
         } catch (Exception e) {
-            throw new CoolException("搴撲綅鑾峰彇澶辫触锛侊紒锛�");
+            log.error(e.getMessage());
+            throw new CoolException("鍑哄簱浠诲姟鐢熸垚澶辫触锛侊紒锛�");
         }
         //TODO 1. 鏍规嵁娉㈡鏄庣粏鐢熸垚鍑哄簱浠诲姟
         // 2. 鏍规嵁鐗╂枡SKU瀵绘壘绗﹀悎鐗╂枡搴撲綅  {1. 鏍规嵁鐗╂枡缂栫爜锛屾壒娆★紝鍔ㄦ�佸瓧娈� 鏌ヨ绗﹀悎鐨勫簱浣嶏紝鍐嶆牴鎹簱浣嶄腑鐗╂枡鐨勬暟閲忛�夋嫨鏈�閫傚悎鐨勫簱浣� 2. 鍒ゆ柇褰撳墠璁㈠崟鏄叏鎷栧嚭搴撹繕鏄嫞鏂欏叆搴搣
         // 3. 淇敼涓诲崟銆佹尝娆℃墽琛屾暟閲�
         // 4. 鍒ゆ柇鍏ㄤ粨鍑哄簱鎴栨嫞鏂欏嚭搴�
         List<Long> orderIds = waveItems.stream().map(WaveItem::getOrderId).collect(Collectors.toList());
-
-        List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, orderIds));
+//        List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, orderIds));
         /**淇敼鍑哄簱鍗曠姸鎬�*/
-        if (!asnOrderService.update(new LambdaQueryWrapper<AsnOrder>()
-                .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)
-                .in(AsnOrder::getId, orders))) {
+        if (!asnOrderService.update(new LambdaUpdateWrapper<AsnOrder>()
+                .set(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)
+                .in(AsnOrder::getId, orderIds))) {
             throw new CoolException("鍑哄簱鍗曟嵁鐘舵�佷慨鏀瑰け璐ワ紒锛�");
         }
-        /**淇敼娉㈡鍗曟嵁鎵ц鐘舵��*/
-        if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).eq(Wave::getId, wave.getId()))) {
-            throw new CoolException("娉㈡鐘舵�佷慨鏀瑰け璐ワ紒锛�");
-        }
+//        /**淇敼娉㈡鍗曟嵁鎵ц鐘舵��*/
+//        if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).eq(Wave::getId, waves.getId()))) {
+//            throw new CoolException("娉㈡鐘舵�佷慨鏀瑰け璐ワ紒锛�");
+//        }
         return R.ok();
     }
 
@@ -113,7 +117,7 @@
             if (locItems.isEmpty()) {
                 continue;
             }
-            List<Long> list = locItems.stream().map(LocItem::getId).collect(Collectors.toList());
+            List<Long> list = locItems.stream().map(LocItem::getLocId).collect(Collectors.toList());
             /**鏍规嵁渚涘簲鍟嗘壒娆★紝鐗╂枡鐮侊紝 鍔ㄦ�佸瓧娈垫煡璇㈡寚瀹氱殑鐗╂枡搴撳瓨淇℃伅*/
             List<LocItem> items = locItemService.list(new LambdaQueryWrapper<LocItem>()
                     .eq(LocItem::getSplrBatch, param.getSplrBatch())
@@ -185,7 +189,7 @@
             /**淇敼娉㈡鎵ц鏁伴噺*/
             taskItems.forEach(item -> {
                 boolean update = waveItemService.update(new LambdaUpdateWrapper<WaveItem>()
-                        .eq(WaveItem::getWaveId, item.getSource())
+                        .eq(WaveItem::getId, item.getSource())
                         .set(WaveItem::getWorkQty, item.getAnfme()));
                 if (!update) {
                     throw new CoolException("娉㈡鎵ц鏁伴噺淇敼澶辫触锛侊紒");
@@ -195,7 +199,7 @@
             List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().eq(WaveItem::getWaveId, wave.getId()));
             double sum = waveItems.stream().mapToDouble(WaveItem::getWorkQty).sum();
             /**娉㈡涓诲崟淇℃伅淇敼*/
-            if (!update(new LambdaUpdateWrapper<Wave>()
+            if (!this.update(new LambdaUpdateWrapper<Wave>()
                     .eq(Wave::getId, wave.getId())
                     .set(Wave::getWorkQty, sum)
                     .set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK.val))) {

--
Gitblit v1.9.1