From a0c27ec444c85326efe3bacf3205dfecbd66451d Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期二, 22 四月 2025 09:28:39 +0800
Subject: [PATCH] #新增 1. 新增出库单明细  2. 编码规则bug修改

---
 rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx                                        |  189 +++
 rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx                                        |    7 
 rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx                                      |  204 +++
 rsf-admin/src/page/orders/outStock/AsnWareModal.jsx                                            |  249 ++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java     |  174 +++
 rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java            |    6 
 rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx                                    |    4 
 rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx                                          |  184 +++
 rsf-admin/src/page/orders/outStock/OutOrderItemEdit.jsx                                        |  195 +++
 rsf-admin/src/page/orders/outStock/PrintModal.jsx                                              |  310 +++++
 rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx                                            |  150 ++
 rsf-admin/src/page/orders/outStock/asnOrder.css                                                |    5 
 rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx                                           |  643 ++++++++++++
 rsf-admin/src/page/orders/outStock/index.jsx                                                   |   18 
 rsf-admin/src/i18n/zh.js                                                                       |   37 
 rsf-admin/src/page/orders/outStock/AsnOrderPanel.jsx                                           |  239 ++++
 rsf-admin/src/page/orders/outStock/OutOrderList.jsx                                            |  263 +++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java |  185 +++
 rsf-admin/src/i18n/en.js                                                                       |    1 
 rsf-admin/src/page/ResourceContent.js                                                          |    4 
 rsf-admin/.env                                                                                 |    2 
 21 files changed, 3,062 insertions(+), 7 deletions(-)

diff --git a/rsf-admin/.env b/rsf-admin/.env
index a8ce658..ccb3f34 100644
--- a/rsf-admin/.env
+++ b/rsf-admin/.env
@@ -1,3 +1,3 @@
-VITE_BASE_IP=192.168.4.24
+VITE_BASE_IP=192.168.4.25
 # VITE_BASE_IP=47.76.147.249
 VITE_BASE_PORT=8080
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index 2840e89..625d37a 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -170,6 +170,7 @@
         logs: 'Logs',
         permissions: 'Permissions',
         delivery: 'Delivery',
+        outStock: 'Out Stock',
     },
     table: {
         field: {
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 82b3d55..7454779 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -171,6 +171,7 @@
         logs: '鏃ュ織',
         permissions: '鏉冮檺绠$悊',
         delivery: 'DO鍗�',
+        outStock: '鍑哄簱鍗�',
 
     },
     table: {
@@ -531,6 +532,20 @@
                 ntyStatus: "涓婃姤鐘舵��",
                 exceStatus: '鍗曟嵁鐘舵��'
             },
+            outStock: {
+                code: "鍑哄簱鍗曞彿",
+                poCode: "骞冲彴鍗曞彿",
+                poId: "PO鏍囪瘑",
+                type: "绫诲瀷",
+                wkType: "涓氬姟绫诲瀷",
+                anfme: "鏁伴噺",
+                qty: "宸插嚭搴撴暟閲�",
+                logisNo: "鐗╂祦鍗曞彿",
+                arrTime: "棰勮鍒拌揪鏃堕棿",
+                rleStatus: "閲婃斁鐘舵��",
+                ntyStatus: "涓婃姤鐘舵��",
+                exceStatus: '鍗曟嵁鐘舵��'
+            },
             asnOrderItem: {
                 asnId: "涓诲崟鏍囪瘑",
                 asnCode: "涓诲崟缂栫爜",
@@ -554,6 +569,28 @@
                 prodTime: "鐢熶骇鏃ユ湡",
                 platItemId: '琛屽彿'
             },
+            outStockItem: {
+                asnId: "涓诲崟鏍囪瘑",
+                asnCode: "鍗曞彿",
+                poDetlId: "骞冲彴鏄庣粏ID",
+                matnrId: "鐗╂枡鏍囪瘑",
+                maktx: "鐗╂枡鍚嶇О",
+                matnrCode: "鐗╂枡缂栫爜",
+                anfme: "璁″垝鍑哄簱鏁�",
+                stockUnit: "搴撳瓨鍗曚綅",
+                purQty: "涓嬪崟鏁伴噺",
+                purUnit: "鍗曚綅",
+                qty: "瀹屾垚鏁伴噺",
+                splrBatch: "渚涘簲鍟嗘壒娆�",
+                splrCode: "渚涘簲鍟嗙紪鐮�",
+                splrName: "渚涘簲鍟嗗悕绉�",
+                qrcode: "浜岀淮鐮�",
+                barcode: "鏉″舰鐮�",
+                packName: "鍖呰",
+                ntyStatus: "鎶ユ鐘舵��",
+                prodTime: "鐢熶骇鏃ユ湡",
+                platItemId: '琛屽彿'
+            },
             asnOrderLog: {
                 code: "缂栫爜",
                 poCode: "PO缂栫爜",
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 256cf8a..5dd2787 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -42,6 +42,8 @@
 import taskLog from './histories/taskLog';
 import stock from './orders/stock';
 import delivery from './orders/delivery';
+import outStock from './orders/outStock';
+
 
 const ResourceContent = (node) => {
     switch (node.component) {
@@ -121,6 +123,8 @@
             return stock;
         case 'delivery':
             return delivery;
+        case 'outStock':
+            return outStock;
         default:
             return {
                 list: ListGuesser,
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/AsnOrderPanel.jsx b/rsf-admin/src/page/orders/outStock/AsnOrderPanel.jsx
new file mode 100644
index 0000000..4649e7f
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/AsnOrderPanel.jsx
@@ -0,0 +1,239 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import { Box, Card, CardContent, Grid, Typography, Button, TextField, Tooltip, Paper, TableContainer, Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
+import {
+    useTranslate,
+    useRecordContext,
+    useNotify,
+    useRefresh,
+    useListContext,
+} from 'react-admin';
+import PanelTypography from "../../components/PanelTypography";
+import * as Common from '@/utils/common'
+import { styled } from "@mui/material/styles";
+import request from '@/utils/request';
+import debounce from 'lodash/debounce';
+import { DataGrid } from '@mui/x-data-grid';
+import PrintModal from './PrintModal';
+import PrintIcon from '@mui/icons-material/Print';
+const AsnOrderPanel = ({ billReload }) => {
+    const record = useRecordContext();
+    if (!record) return null;
+    const translate = useTranslate();
+    const notify = useNotify();
+    const [rows, setRows] = useState([]);
+    const [maktx, setMaktx] = useState('');
+    const asnId = record.id;
+
+    useEffect(() => {
+        debouncedHttp({ maktx });
+    }, [asnId, maktx]);
+
+    const http = async (parmas) => {
+        const res = await request.post('/asnOrderItem/page', { ...parmas, asnId });
+        if (res?.data?.code === 200) {
+            setRows(res.data.data.records)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+
+    useEffect(() => {
+        billReload.current = http
+    }, []);
+
+
+    const debouncedHttp = useMemo(() => debounce(http, 300), []);
+
+    const columns = [
+        {
+            field: 'asnId',
+            headerName: translate('table.field.asnOrderItem.asnId')
+        },
+        {
+            field: 'asnCode',
+            headerName: translate('table.field.asnOrderItem.asnCode'),
+            width: 150,
+        },
+        // {
+        //     field: 'poDetlId',
+        //     headerName: translate('table.field.asnOrderItem.poDetlId')
+        // },
+        {
+            field: 'poCode',
+            headerName: translate('table.field.asnOrderItem.poDetlCode')
+        },
+        {
+            field: 'matnrCode',
+            headerName: translate('table.field.asnOrderItem.matnrCode'),
+            width: 150,
+        },
+        {
+            field: 'maktx',
+            headerName: translate('table.field.asnOrderItem.maktx'),
+            width: 200,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.asnOrderItem.purQty')
+        },
+        {
+            field: 'stockUnit',
+            headerName: translate('table.field.asnOrderItem.stockUnit')
+        },
+        // {
+        //     field: 'purQty',
+        //     headerName: translate('table.field.asnOrderItem.purQty')
+        // },
+        {
+            field: 'purUnit',
+            headerName: translate('table.field.asnOrderItem.purUnit')
+        },
+        {
+            field: 'qty',
+            headerName: translate('table.field.asnOrderItem.qty')
+        },
+        {
+            field: 'splrBatch',
+            headerName: translate('table.field.asnOrderItem.splrBatch')
+        },
+        {
+            field: 'splrCode',
+            headerName: translate('table.field.asnOrderItem.splrCode')
+        },
+        {
+            field: 'splrName',
+            headerName: translate('table.field.asnOrderItem.splrName')
+        },
+        {
+            field: 'trackCode',
+            headerName: translate('table.field.asnOrderItem.barcode'),
+            width: 150
+        },
+        {
+            field: 'prodTime',
+            headerName: translate('table.field.asnOrderItem.prodTime')
+        },
+        {
+            field: 'packName',
+            headerName: translate('table.field.asnOrderItem.packName')
+        },
+        {
+            field: 'action',
+            headerName: '鎿嶄綔',
+            width: 70,
+            lockPosition: 'left',
+            renderCell: (params) => (
+                <PrintButton rows={[params.row.id]} />
+            ),
+
+        },]
+
+    const [selectedRows, setSelectedRows] = useState([]);
+
+    const handleSelectionChange = (ids) => {
+        setSelectedRows(ids)
+
+    };
+    const maktxChange = (value) => {
+        setMaktx(value)
+    }
+
+
+    return (
+        <Box sx={{
+            position: 'relative',
+            padding: '5px 10px'
+        }}>
+            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px', alignItems: 'center' }}>
+                <TextField value={maktx} onChange={(e) => maktxChange(e.target.value)} label="鎼滅储鐗╂枡" sx={{ width: '300px' }} size="small" />
+
+                <div style={{ display: 'flex', gap: '10px' }}>
+                    <PrintsButton rows={selectedRows} />
+                </div>
+            </div>
+
+
+            <DataGrid
+                sx={{ width: 'calc(100vw - 280px)' }}
+                size="small"
+                rows={rows}
+                columns={columns}
+                disableRowSelectionOnClick
+                checkboxSelection
+                onRowSelectionModelChange={handleSelectionChange}
+                selectionModel={selectedRows}
+                disableColumnMenu={true}
+                disableColumnSorting
+                disableMultipleColumnsSorting
+                columnHeaderHeight={40}
+                rowHeight={42}
+                initialState={{
+                    pagination: {
+                        paginationModel: {
+                            pageSize: 10,
+                        },
+                    },
+                }}
+                pageSizeOptions={[10, 25, 50]}
+            />
+        </Box >
+
+    );
+};
+
+export default AsnOrderPanel;
+
+const PrintsButton = ({ rows }) => {
+    const record = useRecordContext();
+    const { resource, selectedIds } = useListContext();
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const translate = useTranslate();
+
+    const [createDialog, setCreateDialog] = useState(false);
+
+    const modalChange = () => {
+        if (rows?.length === 0) {
+            notify('璇烽�夋嫨鐗╂枡');
+            return;
+        } else {
+            setCreateDialog(true)
+        }
+
+    }
+
+    return (
+        <>
+            <Button size="small" color="secondary" onClick={modalChange} startIcon={<PrintIcon />}>{translate("toolbar.batchPrint")}</Button>
+
+            <PrintModal
+                open={createDialog}
+                setOpen={setCreateDialog}
+                rows={rows}
+            />
+        </>
+    )
+}
+
+const PrintButton = ({ rows }) => {
+    const record = useRecordContext();
+
+    const notify = useNotify();
+    const refresh = useRefresh();
+
+    const [createDialog, setCreateDialog] = useState(false);
+    const translate = useTranslate();
+
+    return (
+        <>
+            <Button size="small" color="secondary" onClick={() => setCreateDialog(true)} startIcon={<PrintIcon />}>{translate("toolbar.print")}</Button>
+
+            <PrintModal
+                open={createDialog}
+                setOpen={setCreateDialog}
+                rows={rows}
+            />
+        </>
+    )
+}
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
new file mode 100644
index 0000000..28cd69d
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx
@@ -0,0 +1,184 @@
+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,
+    Stack,
+    Grid,
+    Box,
+} from '@mui/material';
+import DialogCloseButton from "../components/DialogCloseButton";
+import StatusSelectInput from "../components/StatusSelectInput";
+import MemoInput from "../components/MemoInput";
+
+const OutOrderCreate = (props) => {
+    const { open, setOpen } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const handleSuccess = async (data) => {
+        setOpen(false);
+        notify('common.response.success');
+    };
+
+    const handleError = async (error) => {
+        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
+    };
+
+    return (
+        <>
+            <CreateBase
+                record={{}}
+                transform={(data) => {
+                    return data;
+                }}
+                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
+            >
+                <Dialog
+                    open={open}
+                    onClose={handleClose}
+                    aria-labelledby="form-dialog-title"
+                    fullWidth
+                    disableRestoreFocus
+                    maxWidth="md"   // '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.asnOrder.code"
+                                        source="code"
+                                        parse={v => v}
+                                        autoFocus
+                                    />
+                                </Grid> */}
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrder.poCode"
+                                        source="poCode"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.asnOrder.poId"
+                                        source="poId"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrder.type"
+                                        source="type"
+                                        parse={v => v}
+                                        validate={required()}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrder.wkType"
+                                        source="wkType"
+                                        parse={v => v}
+                                        validate={required()}
+                                    />
+                                </Grid>
+                                {/* <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.asnOrder.anfme"
+                                        source="anfme"
+                                        validate={required()}
+                                    />
+                                </Grid> */}
+                                {/* <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.asnOrder.qty"
+                                        source="qty"
+                                        validate={required()}
+                                    />
+                                </Grid> */}
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrder.logisNo"
+                                        source="logisNo"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <DateInput
+                                        label="table.field.asnOrder.arrTime"
+                                        source="arrTime"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <SelectInput
+                                        label="table.field.asnOrder.rleStatus"
+                                        source="rleStatus"
+                                        choices={[
+                                            { id: 0, name: ' 姝e父' },
+                                            { id: 1, name: ' 宸查噴鏀�' },
+                                        ]}
+                                    />
+                                </Grid>
+
+                                <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 />
+                                    </Stack>
+                                </Grid>
+                            </Grid>
+                        </DialogContent>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                                <SaveButton />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </Dialog>
+            </CreateBase>
+        </>
+    )
+}
+
+export default OutOrderCreate;
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx b/rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx
new file mode 100644
index 0000000..ddfa812
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx
@@ -0,0 +1,150 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    Edit,
+    SimpleForm,
+    useTranslate,
+    TextInput,
+    DateInput,
+    SelectInput,
+    AutocompleteInput,
+    SaveButton,
+    Toolbar,
+    required,
+    DeleteButton,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography } from '@mui/material';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import EditBaseAside from "../../components/EditBaseAside";
+import CustomerTopToolBar from "../../components/EditTopToolBar";
+import AsnOrderItemList from "./AsnOrderItemList";
+
+const FormToolbar = () => {
+    const { getValues } = useFormContext();
+
+    return (
+        <Toolbar sx={{ justifyContent: 'end' }}>
+            <></>
+            {/* <SaveButton />
+            <DeleteButton mutationMode="optimistic" /> */}
+        </Toolbar>
+    )
+}
+
+const OutOrderEdit = () => {
+    const translate = useTranslate();
+    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_order_type')) || [];
+    const business = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
+
+    return (
+        <>
+            <Edit
+                redirect="list"
+                mutationMode={EDIT_MODE}
+                actions={<CustomerTopToolBar />}
+                aside={<EditBaseAside />}
+            >
+                <SimpleForm
+                    shouldUnregister
+                    warnWhenUnsavedChanges
+                    toolbar={<FormToolbar />}
+                    mode="onTouched"
+                    defaultValues={{}}
+                >
+                    <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}
+                        sx={{
+                            "& .MuiFormLabel-root.MuiInputLabel-root.Mui-disabled": {
+                                bgcolor: 'white',
+                                WebkitTextFillColor: "rgba(0, 0, 0)"
+                            },
+
+                            "& .MuiInputBase-input.MuiFilledInput-input.Mui-disabled": {
+                                bgcolor: 'white',
+                                WebkitTextFillColor: "rgba(0, 0, 0)"
+                            },
+                            "& .MuiFilledInput-root.MuiInputBase-sizeSmall": {
+                                bgcolor: 'white',
+                            }
+                        }}
+                    >
+                        <Grid item xs={24} md={12} >
+                            <Typography variant="h6" gutterBottom>
+                                {translate('common.edit.title.main')}
+                            </Typography>
+                            <Stack direction='row' gap={2}>
+                                <TextInput
+                                    label="table.field.asnOrder.code"
+                                    source="code"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.asnOrder.poCode"
+                                    source="poCode"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <AutocompleteInput
+                                    choices={dicts}
+                                    optionText="label"
+                                    label="table.field.asnOrder.type"
+                                    source="type"
+                                    optionValue="value"
+                                    parse={v => v}
+                                    readOnly
+                                />
+                                <AutocompleteInput
+                                    choices={business}
+                                    optionText="label"
+                                    label="table.field.asnOrder.wkType"
+                                    source="wkType"
+                                    optionValue="value"
+                                    parse={v => v}
+                                    readOnly
+                                />
+                            </Stack>
+                            <Stack direction='row' gap={2}>
+                                <TextInput
+                                    label="table.field.asnOrder.logisNo"
+                                    source="logisNo"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.asnOrder.anfme"
+                                    source="anfme"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.asnOrder.qty"
+                                    source="qty"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <DateInput
+                                    label="table.field.asnOrder.arrTime"
+                                    source="arrTime"
+                                    readOnly
+                                />
+                                <SelectInput
+                                    label="table.field.asnOrder.rleStatus"
+                                    source="rleStatus"
+                                    readOnly
+                                    choices={[
+                                        { id: 0, name: ' 姝e父' },
+                                        { id: 1, name: ' 宸查噴鏀�' },
+                                    ]}
+                                    validate={required()}
+                                />
+                            </Stack>
+                        </Grid>
+                    </Grid>
+                </SimpleForm>
+            </Edit >
+            <AsnOrderItemList />
+        </>
+    )
+}
+
+export default OutOrderEdit;
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx b/rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx
new file mode 100644
index 0000000..e9a378d
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx
@@ -0,0 +1,204 @@
+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,
+    Stack,
+    Grid,
+    Box,
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+import MemoInput from "../../components/MemoInput";
+
+const OutOrderItemCreate = (props) => {
+    const { open, setOpen, record } = props;
+    const translate = useTranslate();
+    const notify = useNotify();
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const handleSuccess = async (data) => {
+        setOpen(false);
+        notify('common.response.success');
+    };
+
+    const handleError = async (error) => {
+        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
+    };
+
+    return (
+        <>
+            <CreateBase
+                resource="asnOrderItem"
+                record={{}}
+                transform={(data) => {
+                    return data;
+                }}
+                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
+            >
+                <Dialog
+                    open={open}
+                    onClose={handleClose}
+                    aria-labelledby="form-dialog-title"
+                    fullWidth
+                    disableRestoreFocus
+                    maxWidth="md"   // '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>
+                                <Grid item xs={6} display="flex" gap={2}>
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.asnId"
+                                        source="asnId"
+                                        readOnly
+                                        hidden
+                                        defaultValue={record?.id}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.asnCode"
+                                        source="asnCode"
+                                        readOnly
+                                        defaultValue={record?.code}
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={2}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.poDetlId"
+                                        source="poDetlId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.poDetlCode"
+                                        source="poDetlCode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.matnrId"
+                                        source="matnrId"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.maktx"
+                                        source="maktx"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.anfme"
+                                        source="anfme"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.stockUnit"
+                                        source="stockUnit"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.purQty"
+                                        source="purQty"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.purUnit"
+                                        source="purUnit"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.qty"
+                                        source="qty"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.splrCode"
+                                        source="splrCode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.splrName"
+                                        source="splrName"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.qrcode"
+                                        source="qrcode"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.barcode"
+                                        source="barcode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.packName"
+                                        source="packName"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+
+                                <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 />
+                                    </Stack>
+                                </Grid>
+                            </Grid>
+                        </DialogContent>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                                <SaveButton />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </Dialog>
+            </CreateBase>
+        </>
+    )
+}
+
+export default OutOrderItemCreate;
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderItemEdit.jsx b/rsf-admin/src/page/orders/outStock/OutOrderItemEdit.jsx
new file mode 100644
index 0000000..800b76b
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderItemEdit.jsx
@@ -0,0 +1,195 @@
+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,
+    required,
+    Form,
+    useRecordContext,
+    useGetOne,
+    DeleteButton,
+    EditBase,
+    ReferenceField,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import EditBaseAside from "../../components/EditBaseAside";
+import CustomerTopToolBar from "../../components/EditTopToolBar";
+import MemoInput from "../../components/MemoInput";
+import StatusSelectInput from "../../components/StatusSelectInput";
+
+const FormToolbar = () => {
+    const { getValues } = useFormContext();
+
+    return (
+        <Toolbar sx={{ justifyContent: 'end' }}>
+            <SaveButton />
+            <DeleteButton mutationMode="optimistic" />
+        </Toolbar>
+    )
+}
+
+const OutOrderItemEdit = (props) => {
+    const { open, setOpen, record } = props;
+    const translate = useTranslate();
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+    const { data, isPending, } = useGetOne('asnOrderItem', { id: record?.id });
+    if (data == null || data == undefined) { return }
+
+    return (
+        <Dialog
+            open={open}
+            onClose={handleClose}
+            aria-labelledby="form-dialog-title"
+            fullWidth
+            disableRestoreFocus
+            maxWidth="md"
+        >
+            <DialogTitle id="form-dialog-title" sx={{
+                position: 'sticky',
+                top: 0,
+                backgroundColor: 'background.paper',
+                zIndex: 1000
+            }}
+            >
+                {translate('update.title')}
+                <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                    <DialogCloseButton onClose={handleClose} />
+                </Box>
+            </DialogTitle>
+            <DialogContent sx={{ mt: 2 }}>
+                <EditBase
+                    id={record?.id}
+                    resource="asnOrderItem"
+                    mutationMode={EDIT_MODE}
+                    actions={<CustomerTopToolBar />}
+                >
+                    <Form
+                        shouldUnregister
+                        warnWhenUnsavedChanges
+                        mode="onTouched"
+                        defaultValues={{}}
+                    >
+                        <Grid container width={{ xs: '100%', xl: '100%' }}>
+                            <Grid item xs={24} md={14}>
+                                <Stack direction='row' gap={2}>
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.asnId"
+                                        source="asnId"
+                                        readOnly
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.asnCode"
+                                        source="asnCode"
+                                        readOnly
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.poDetlId"
+                                        source="poDetlId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.poDetlCode"
+                                        source="poDetlCode"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.matnrId"
+                                        source="matnrId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.maktx"
+                                        source="maktx"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.anfme"
+                                        source="anfme"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.stockUnit"
+                                        source="stockUnit"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.purQty"
+                                        source="purQty"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.purUnit"
+                                        source="purUnit"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.asnOrderItem.qty"
+                                        source="qty"
+                                        readOnly
+                                    />
+                                    <ReferenceInput source="splrName" label="table.field.asnOrderItem.splrName" reference="companys" filter={{type: 'supplier'}}>
+                                        <AutocompleteInput optionText="name" label="table.field.asnOrderItem.splrName" />
+                                    </ReferenceInput>
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <TextInput
+                                        label="table.field.asnOrderItem.qrcode"
+                                        source="qrcode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.barcode"
+                                        source="trackCode"
+                                        parse={v => v}
+                                        readOnly
+                                    />
+                                    <TextInput
+                                        label="table.field.asnOrderItem.packName"
+                                        source="packName"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                            </Grid>
+                        </Grid>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                                <SaveButton type="button" mutationOptions={{
+                                    onSuccess: () => {
+                                        setOpen(false)
+                                    }
+                                }} />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </EditBase >
+            </DialogContent>
+        </Dialog>
+    )
+}
+
+export default OutOrderItemEdit;
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
new file mode 100644
index 0000000..4d73b97
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
@@ -0,0 +1,189 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import { useNavigate, useLocation } from 'react-router-dom';
+import {
+  List,
+  DatagridConfigurable,
+  SearchInput,
+  TopToolbar,
+  SelectColumnsButton,
+  EditButton,
+  FilterButton,
+  CreateButton,
+  ExportButton,
+  BulkDeleteButton,
+  WrapperField,
+  useRecordContext,
+  useTranslate,
+  useNotify,
+  useListContext,
+  FunctionField,
+  TextField,
+  NumberField,
+  DateField,
+  BooleanField,
+  ReferenceField,
+  TextInput,
+  DateTimeInput,
+  DateInput,
+  SelectInput,
+  NumberInput,
+  ReferenceInput,
+  ReferenceArrayInput,
+  AutocompleteInput,
+  DeleteButton,
+  Button,
+  useEditContext,
+  useGetRecordId,
+  useGetOne
+} from 'react-admin';
+import { Box, Typography, Card, Stack, Dialog, DialogActions, DialogTitle } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import OutOrderItemCreate from "./OutOrderItemCreate";
+import EmptyData from "../../components/EmptyData";
+import MyCreateButton from "../../components/MyCreateButton";
+import MyExportButton from '../../components/MyExportButton';
+import PageDrawer from "../../components/PageDrawer";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
+import OutOrderItemEdit from "./OutOrderItemEdit";
+import ImportButton from "../../components/ImportButton";
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+  '& .css-1vooibu-MuiSvgIcon-root': {
+    height: '.9em',
+
+  },
+  '& .RaDatagrid-row': {
+    cursor: 'auto'
+  },
+  '& .column-name': {
+  },
+  '& .opt': {
+    width: 200
+  },
+}));
+
+const filters = [
+  <SearchInput source="condition" alwaysOn />,
+  <NumberInput source="asnId" label="table.field.asnOrderItem.asnId" />,
+  <TextInput source="asnCode" label="table.field.asnOrderItem.asnCode" />,
+  <TextInput source="poDetlId" label="table.field.asnOrderItem.poDetlId" />,
+  <TextInput source="poDetlCode" label="table.field.asnOrderItem.poDetlCode" />,
+  <TextInput source="matnrId" label="table.field.asnOrderItem.matnrId" />,
+  <TextInput source="maktx" label="table.field.asnOrderItem.maktx" />,
+  <NumberInput source="anfme" label="table.field.asnOrderItem.anfme" />,
+  <TextInput source="stockUnit" label="table.field.asnOrderItem.stockUnit" />,
+  <NumberInput source="purQty" label="table.field.asnOrderItem.purQty" />,
+  <TextInput source="purUnit" label="table.field.asnOrderItem.purUnit" />,
+  <NumberInput source="qty" label="table.field.asnOrderItem.qty" />,
+  <TextInput source="splrCode" label="table.field.asnOrderItem.splrCode" />,
+  <TextInput source="splrName" label="table.field.asnOrderItem.splrName" />,
+  <TextInput source="qrcode" label="table.field.asnOrderItem.qrcode" />,
+  <TextInput source="trackCode" label="table.field.asnOrderItem.barcode" />,
+  <TextInput source="packName" label="table.field.asnOrderItem.packName" />,
+  <TextInput label="common.field.memo" source="memo" />,
+  <SelectInput
+    label="common.field.status"
+    source="status"
+    choices={[
+      { id: '1', name: 'common.enums.statusTrue' },
+      { id: '0', name: 'common.enums.statusFalse' },
+    ]}
+    resettable
+  />,
+]
+
+const OutOrderItemList = () => {
+  const translate = useTranslate();
+  const [createDialog, setCreateDialog] = useState(false);
+  const [editDialog, setEditDialog] = useState(false);
+  const [drawerVal, setDrawerVal] = useState(false);
+  const [select, setSelect] = useState({});
+  const asnId = useGetRecordId();
+  const { data: dicts, isPending, error } = useGetOne('asnOrder', { id: asnId });
+
+  return (
+    <>
+      <Box display="flex">
+        <List
+          resource="asnOrderItem"
+          sx={{
+            flexGrow: 1,
+            transition: (theme) =>
+              theme.transitions.create(['all'], {
+                duration: theme.transitions.duration.enteringScreen,
+              }),
+            marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
+          }}
+          title={"menu.asnOrderItem"}
+          empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+          filter={{ asnId: asnId, deleted: 0 }}
+          filters={filters}
+          sort={{ field: "create_time", order: "desc" }}
+          actions={(
+            <TopToolbar>
+              <FilterButton />
+              <MyCreateButton onClick={() => { setCreateDialog(true) }} />
+              <SelectColumnsButton preferenceKey='asnOrderItem' />
+              {/* <MyExportButton /> */}
+            </TopToolbar>
+          )}
+          perPage={DEFAULT_ITEM_PAGE_SIZE}
+        >
+          <StyledDatagrid
+            preferenceKey='asnOrderItem'
+            bulkActionButtons={false}
+            rowClick={(id, resource, record) => {
+              setSelect(record)
+              setEditDialog(true)
+            }}
+             omit={['id', 'createTime', 'createBy', 'memo', 'poDetlId', 'matnrId', 'asnId']}
+          >
+            <NumberField source="id" />
+            <NumberField source="asnId" label="table.field.asnOrderItem.asnId" />
+            <TextField source="asnCode" label="table.field.asnOrderItem.asnCode" />
+            <TextField source="poDetlId" label="table.field.asnOrderItem.poDetlId" />
+            <TextField source="poDetlCode" label="table.field.asnOrderItem.poDetlCode" />
+            <TextField source="matnrId" label="table.field.asnOrderItem.matnrId" />
+            <TextField source="matnrCode" label="table.field.asnOrderItem.matnrCode" />
+            <TextField source="maktx" label="table.field.asnOrderItem.maktx" />
+            <NumberField source="anfme" label="table.field.asnOrderItem.anfme" />
+            <TextField source="stockUnit" label="table.field.asnOrderItem.stockUnit" />
+            <NumberField source="purQty" label="table.field.asnOrderItem.purQty" />
+            <TextField source="purUnit" label="table.field.asnOrderItem.purUnit" />
+            <NumberField source="qty" label="table.field.asnOrderItem.qty" />
+            <TextField source="splrCode" label="table.field.asnOrderItem.splrCode" />
+            <TextField source="splrName" label="table.field.asnOrderItem.splrName" />
+            <TextField source="qrcode" label="table.field.asnOrderItem.qrcode" />
+            <TextField source="trackCode" label="table.field.asnOrderItem.barcode" />
+            <TextField source="packName" label="table.field.asnOrderItem.packName" />
+            <TextField source="updateBy$" label="common.field.updateBy" />
+            <DateField source="updateTime" label="common.field.updateTime" showTime />
+            <TextField source="createBy$" label="common.field.createBy" />
+            <DateField source="createTime" label="common.field.createTime" showTime />
+            <BooleanField source="status$" label="common.field.status" sortable={false} />
+            <TextField source="memo" label="common.field.memo" sortable={false} />
+          </StyledDatagrid>
+        </List>
+        <OutOrderItemCreate
+          open={createDialog}
+          setOpen={setCreateDialog}
+          record={dicts}
+        />
+        <OutOrderItemEdit
+          open={editDialog}
+          setOpen={setEditDialog}
+          record={select}
+        />
+        <PageDrawer
+          title='AsnOrderItem Detail'
+          drawerVal={drawerVal}
+          setDrawerVal={setDrawerVal}
+        >
+        </PageDrawer>
+      </Box>
+    </>
+  )
+}
+OutOrderItemList.Context = React.createContext()
+
+export default OutOrderItemList;
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
new file mode 100644
index 0000000..f5b9dd5
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -0,0 +1,263 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import { useLocation, useNavigate } from 'react-router-dom';
+import {
+  List,
+  DatagridConfigurable,
+  SearchInput,
+  TopToolbar,
+  SelectColumnsButton,
+  EditButton,
+  FilterButton,
+  CreateButton,
+  ExportButton,
+  BulkDeleteButton,
+  useDataProvider,
+  WrapperField,
+  useRecordContext,
+  useTranslate,
+  useNotify,
+  useRefresh,
+  useListContext,
+  FunctionField,
+  TextField,
+  NumberField,
+  DateField,
+  BooleanField,
+  ReferenceField,
+  TextInput,
+  DateTimeInput,
+  DateInput,
+  SelectInput,
+  NumberInput,
+  ReferenceInput,
+  ReferenceArrayInput,
+  AutocompleteInput,
+  DeleteButton,
+  Button,
+  useRedirect,
+  useUnselectAll,
+} from 'react-admin';
+import { Box, Typography, Card, Stack } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import AsnOrderModal from "./AsnOrderModal";
+import EmptyData from "../../components/EmptyData";
+import MyCreateButton from "../../components/MyCreateButton";
+import MyExportButton from '../../components/MyExportButton';
+import BillStatusField from '../../components/BillStatusField';
+import ConfirmButton from '../../components/ConfirmButton';
+import PageDrawer from "../../components/PageDrawer";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import ConstructionIcon from "@mui/icons-material/Construction";
+import EditIcon from '@mui/icons-material/Edit';
+import TaskIcon from '@mui/icons-material/Task';
+import CloseIcon from '@mui/icons-material/Close';
+import request from '@/utils/request';
+import DictionarySelect from "../../components/DictionarySelect";
+import ExitToAppIcon from '@mui/icons-material/ExitToApp';
+import ImportButton from "../../components/ImportButton";
+import DetailsIcon from '@mui/icons-material/Details';
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+  '& .css-1vooibu-MuiSvgIcon-root': {
+    height: '.9em'
+  },
+  '& .RaDatagrid-row': {
+    cursor: 'auto'
+  },
+  '& .column-name': {
+  },
+  '& .opt': {
+    width: 220
+  },
+  '& .wkType': {
+    width: 110
+  },
+  '& .status': {
+    width: 90
+  },
+}));
+
+const filters = [
+  <SearchInput source="condition" alwaysOn />,
+  <TextInput source="code" label="table.field.asnOrder.code" />,
+  <TextInput source="poCode" label="table.field.asnOrder.poCode" />,
+  <NumberInput source="poId" label="table.field.asnOrder.poId" />,
+  <TextInput source="type" label="table.field.asnOrder.type" />,
+  <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type' }} label="table.field.asnOrder.wkType">
+    <AutocompleteInput label="table.field.asnOrder.wkType" optionValue="value" />
+  </ReferenceInput>,
+  <NumberInput source="anfme" label="table.field.asnOrder.anfme" />,
+  <NumberInput source="qty" label="table.field.asnOrder.qty" />,
+  <TextInput source="logisNo" label="table.field.asnOrder.logisNo" />,
+  <DateInput source="arrTime" label="table.field.asnOrder.arrTime" />,
+  <SelectInput source="rleStatus" label="table.field.asnOrder.rleStatus"
+    choices={[
+      { id: 0, name: ' 姝e父' },
+      { id: 1, name: ' 宸查噴鏀�' },
+    ]}
+  />,
+
+  <TextInput label="common.field.memo" source="memo" />,
+  <DictionarySelect
+    label='table.field.asnOrder.exceStatus'
+    name="exceStatus"
+    dictTypeCode="sys_asn_exce_status"
+    alwaysOn
+  />,
+
+]
+
+const OutOrderList = (props) => {
+  const translate = useTranslate();
+  const [createDialog, setCreateDialog] = useState(false);
+  const [drawerVal, setDrawerVal] = useState(false);
+  const [modalType, setmodalType] = useState(0);
+  const [select, setSelect] = useState(0);
+  const billReload = useRef();
+  const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
+  return (
+    <Box display="flex">
+      <List
+        resource="outStock"
+        storeKey='outStock'
+        sx={{
+          flexGrow: 1,
+          transition: (theme) =>
+            theme.transitions.create(['all'], {
+              duration: theme.transitions.duration.enteringScreen,
+            }),
+          marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
+        }}
+        title={"menu.outStock"}
+        empty={<EmptyData onClick={() => { setCreateDialog(true); setmodalType(0) }} />}
+        filters={filters}
+        filter={{deleted: 0, type: 'out'}}
+        sort={{ field: "create_time", order: "desc" }}
+        actions={(
+          <TopToolbar>
+            <FilterButton />
+            <MyCreateButton onClick={() => { setCreateDialog(true); setmodalType(0) }} />
+            <SelectColumnsButton preferenceKey='outStock' />
+            <ImportButton value={'asnOrderItem'}  />
+            <MyExportButton />
+          </TopToolbar>
+        )}
+        perPage={DEFAULT_PAGE_SIZE}
+      >
+        <StyledDatagrid
+          sx={{ width: '100%' }}
+          preferenceKey='outStock'
+          bulkActionButtons={
+            <>
+              <MyExportButton />
+              {/* <BtnBulkExport></BtnBulkExport> */}
+              <BulkDeleteButton mutationMode={OPERATE_MODE}
+              />
+            </>}
+          rowClick={false}
+          expandSingle={true}
+          omit={['id', 'createTime', 'createBy', 'memo', 'poId', 'rleStatus$']}
+        >
+          <NumberField source="id" />
+          <TextField source="code" label="table.field.outStock.code" />
+          <TextField source="poCode" label="table.field.outStock.poCode" />
+          <NumberField source="poId" label="table.field.outStock.poId" />
+          <TextField source="type$" label="table.field.outStock.type" />
+          <TextField cellClassName="wkType" source="wkType$" label="table.field.outStock.wkType" />
+          <NumberField source="anfme" label="table.field.outStock.anfme" />
+          <NumberField source="qty" label="table.field.outStock.qty" />
+          <TextField source="logisNo" label="table.field.outStock.logisNo" />
+          <TextField source="rleStatus$" label="table.field.outStock.rleStatus" sortable={false} />
+          <TextField source="updateBy$" label="common.field.updateBy" />
+          <DateField source="updateTime" label="common.field.updateTime" showTime />
+          <TextField source="createBy$" label="common.field.createBy" />
+          <DateField source="createTime" label="common.field.createTime" showTime />
+          <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" >
+            <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
+            <MyButton setCreateDialog={setCreateDialog} setmodalType={setmodalType} />
+            {/* <CompleteButton /> */}
+          </WrapperField>
+        </StyledDatagrid>
+      </List>
+      <AsnOrderModal
+        open={createDialog}
+        setOpen={setCreateDialog}
+        asnId={modalType}
+        billReload={billReload}
+      />
+      <PageDrawer
+        title='AsnOrder Detail'
+        drawerVal={drawerVal}
+        setDrawerVal={setDrawerVal}
+      >
+      </PageDrawer>
+    </Box >
+  )
+}
+export default OutOrderList;
+
+const MyButton = ({ setCreateDialog, setmodalType }) => {
+  const record = useRecordContext();
+  const handleEditClick = (btn) => {
+    btn.stopPropagation();
+    const id = record.id;
+    setmodalType(id);
+    setCreateDialog(true);
+
+  };
+  return (
+    <Button
+      color="primary"
+      startIcon={<EditIcon />}
+      onClick={(btn) => handleEditClick(btn)}
+      sx={{ ml: 1 }}
+      label={'ra.action.edit'}
+    >
+    </Button>
+  )
+}
+
+const CompleteButton = () => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+  const requestComplete = async () => {
+    const { data: { code, data, msg } } = await request.post(`/asnOrder/complete/${record.id}`);
+
+    if (code === 200) {
+      notify(msg);
+      refresh()
+    } else {
+      notify(msg);
+    }
+  }
+
+  return (
+    record.exceStatus === 1 && (record.anfme === record.qty ? <Button onClick={requestComplete} label={"toolbar.complete"} color="success">
+      <TaskIcon />
+    </Button> : <ConfirmButton label={"toolbar.complete"} color="success" data={'褰撳墠鏀惰揣鏁伴噺灏忎簬璁″垝鏁伴噺锛屾槸鍚︾‘璁ゅ畬鎴�'} startIcon={<TaskIcon />} onConfirm={requestComplete} />)
+
+  )
+}
+
+const CloseButton = () => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+  const requestClose = async () => {
+    const { data: { code, data, msg } } = await request.post(`/asnOrder/close/${record.id}`);
+
+    if (code === 200) {
+      notify(msg);
+      refresh()
+    } else {
+      notify(msg);
+    }
+  }
+
+  return (
+    <ConfirmButton label={"toolbar.close"} color="error" data={'纭鏄惁鍏抽棴锛�'} startIcon={<CloseIcon />} onConfirm={requestClose} />
+  )
+}
diff --git a/rsf-admin/src/page/orders/outStock/PrintModal.jsx b/rsf-admin/src/page/orders/outStock/PrintModal.jsx
new file mode 100644
index 0000000..2ad47fd
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/PrintModal.jsx
@@ -0,0 +1,310 @@
+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,
+    Grid,
+    TextField,
+    Box,
+    Button,
+    Radio,
+    RadioGroup,
+    FormControlLabel,
+    FormControl,
+    FormLabel,
+    TableRow,
+    TableCell,
+    Tooltip,
+    IconButton,
+    styled
+
+
+} from '@mui/material';
+import DialogCloseButton from "@/page/components/DialogCloseButton";
+import DictionarySelect from "@/page/components/DictionarySelect";
+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 _ from 'lodash';
+import { DataGrid } from '@mui/x-data-grid';
+import StatusSelectInput from "@/page/components/StatusSelectInput";
+
+import { useReactToPrint } from "react-to-print";
+import jsbarcode from 'jsbarcode'
+import { el } from "date-fns/locale";
+
+const PrintModal = ({ open, setOpen, rows }) => {
+    const refresh = useRefresh();
+    const translate = useTranslate();
+    const notify = useNotify();
+    const contentRef = useRef(null);
+    const reactToPrintFn = useReactToPrint({ contentRef });
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const [value, setValue] = useState('temp1');
+
+    const handleChange = (event) => {
+        setValue(event.target.value);
+    };
+
+    const handlePrint = () => {
+        // handleClose()
+        reactToPrintFn()
+    };
+
+    return (
+        <Dialog open={open} maxWidth="sm" fullWidth>
+            <DialogCloseButton onClose={handleClose} />
+            <DialogTitle>{translate('toolbar.print')}</DialogTitle>
+            <DialogContent >
+                <FormControl >
+                    <RadioGroup
+                        row
+                        aria-labelledby="demo-controlled-radio-buttons-group"
+                        name="controlled-radio-buttons-group"
+                        value={value}
+                        onChange={handleChange}
+                        size="small"
+                        sx={{ justifyContent: 'center' }}
+                    >
+                        <FormControlLabel value="temp1" control={<Radio />} label="妯℃澘1" size="small" />
+                    </RadioGroup>
+                </FormControl>
+
+                <Box>
+                    <div style={{ textAlign: 'center', display: 'flex', justifyContent: 'center' }}>
+                        <table
+                            className="contain"
+                            style={{
+                                overflow: 'hidden',
+                                fontSize: 'small',
+                                tableLayout: 'fixed',
+                                width: '280px',
+                                borderCollapse: 'collapse', // 鍚堝苟杈规
+                                border: '1px solid black' // 璁剧疆琛ㄦ牸鏁翠綋杈规
+                            }}
+                        >
+                            <tbody>
+                                <tr style={{ height: '74px' }}>
+                                    <td
+                                        align="center"
+                                        colSpan={3}
+                                        style={{ border: '1px solid black' }} // 璁剧疆鍗曞厓鏍艰竟妗�
+                                    >
+                                        鍟嗗搧缂栫爜
+                                    </td>
+                                    <td
+                                        align="center"
+                                        className="barcode"
+                                        colSpan={9}
+                                        style={{ border: '1px solid black' }}
+                                    >
+                                        <img className="template-code" src={'/img/barcode.jpeg'} style={{ width: '90%' }} alt="Barcode" />
+                                        <div style={{ letterSpacing: '2px', marginTop: '1px', textAlign: 'center' }}>
+                                            <span>{'xxxxxx'}</span>
+                                        </div>
+                                    </td>
+                                </tr>
+                                <tr style={{ height: '74px' }}>
+                                    <td
+                                        align="center"
+                                        colSpan={3}
+                                        style={{ border: '1px solid black' }}
+                                    >
+                                        鍟嗗搧
+                                    </td>
+                                    <td
+                                        align="center"
+                                        colSpan={5}
+                                        style={{
+                                            overflow: 'hidden',
+                                            whiteSpace: 'nowrap',
+                                            textOverflow: 'ellipsis',
+                                            border: '1px solid black'
+                                        }}
+                                    >
+                                        {'xxxxxxxx'}
+                                    </td>
+                                    <td
+                                        align="center"
+                                        colSpan={2}
+                                        style={{ border: '1px solid black' }}
+                                    >
+                                        澶囨敞
+                                    </td>
+                                    <td
+                                        align="center"
+                                        colSpan={2}
+                                        style={{ border: '1px solid black' }}
+                                    >
+                                        {'xx'}
+                                    </td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                    <style>{`
+                    @media print {
+                    .print-content {
+                            display: block!important;
+                        }
+                    }`} </style>
+                    <div ref={contentRef} className="print-content" style={{ textAlign: 'center', display: 'none' }}>
+                        <PrintTemp key={'bb'} rows={rows} />
+                    </div>
+                </Box>
+            </DialogContent>
+            <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
+                    <Button onClick={handlePrint} variant="contained" startIcon={<SaveIcon />}>
+                        {translate('toolbar.confirm')}
+                    </Button>
+                </Box>
+            </DialogActions>
+        </Dialog >
+    );
+}
+
+export default PrintModal;
+
+const PrintTemp = ({ rows }) => {
+    const notify = useNotify();
+    const [data, setData] = useState([]);
+    const http = async () => {
+        const res = await request.post(`/asnOrderItem/many/${rows?.join()}`);
+        if (res?.data?.code === 200) {
+            let val = res.data.data.map((el => {
+                return {
+                    barcode: '/img/barcode.jpeg',
+                    code: el.trackCode,
+                    name: el.maktx,
+                    memo: el.memo || ''
+                }
+            }))
+            setData(val)
+            setTimeout(() => {
+                val.forEach((el) => {
+                    jsbarcode(`#barcode${el.code}`, el.code, { height: 30 });
+                });
+            }, 10);
+
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    useEffect(() => {
+        if (rows?.length > 0) {
+            http();
+        }
+
+    }, [rows]);
+
+
+    return (
+        <>
+            {data.map((item, index) => (
+                <table
+                    key={index}
+                    className="contain"
+                    style={{
+                        overflow: 'hidden',
+                        fontSize: 'small',
+                        tableLayout: 'fixed',
+                        width: '520px',
+                        borderCollapse: 'collapse',
+                        borderSpacing: 0,
+                        margin: '0 auto',
+                        marginTop: '10px',
+                    }}
+                >
+                    <tbody>
+                        <tr style={{ height: '74px' }}>
+                            <td align="center" colSpan={3} style={{ border: '1px solid black' }} >
+                                鍟嗗搧缂栫爜
+                            </td>
+                            <td
+                                align="center"
+                                className="barcode"
+                                colSpan={9}
+                                style={{ border: '1px solid black' }}
+                            >
+                                <img id={"barcode" + item.code} style={{ width: '70%', verticalAlign: 'middle' }} />
+                                {/* <img className="template-code" src={item.barcode} style={{ width: '90%', verticalAlign: 'middle' }} alt="Barcode" /> */}
+                                {/* <div style={{ letterSpacing: '2px', marginTop: '1px', textAlign: 'center' }}>
+                                    <span>{item.code}</span>
+                                </div> */}
+                            </td>
+                        </tr>
+                        <tr style={{ height: '74px' }}>
+                            <td
+                                align="center"
+                                colSpan={3}
+                                style={{ border: '1px solid black' }}
+                            >
+                                鍟嗗搧
+                            </td>
+                            <td
+                                align="center"
+                                colSpan={5}
+                                style={{
+                                    overflow: 'hidden',
+                                    whiteSpace: 'nowrap',
+                                    textOverflow: 'ellipsis',
+                                    border: '1px solid black'
+                                }}
+                            >
+                                {item.name}
+                            </td>
+                            <td
+                                align="center"
+                                colSpan={2}
+                                style={{ border: '1px solid black' }}
+                            >
+                                澶囨敞
+                            </td>
+                            <td
+                                align="center"
+                                colSpan={2}
+                                style={{ border: '1px solid black' }}
+                            >
+                                {item.memo}
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            ))}
+
+        </>
+    )
+}
+
+
diff --git a/rsf-admin/src/page/orders/outStock/asnOrder.css b/rsf-admin/src/page/orders/outStock/asnOrder.css
new file mode 100644
index 0000000..0df941e
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/asnOrder.css
@@ -0,0 +1,5 @@
+
+.custom {
+    color: rgb(0, 195, 255) !important;    
+  }
+
diff --git a/rsf-admin/src/page/orders/outStock/index.jsx b/rsf-admin/src/page/orders/outStock/index.jsx
new file mode 100644
index 0000000..d710162
--- /dev/null
+++ b/rsf-admin/src/page/orders/outStock/index.jsx
@@ -0,0 +1,18 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    ListGuesser,
+    EditGuesser,
+    ShowGuesser,
+} from "react-admin";
+
+import OutOrderList from "./OutOrderList";
+import OutOrderEdit from "./OutOrderEdit";
+
+export default {
+    list: OutOrderList,
+    edit: OutOrderEdit,
+    show: ShowGuesser,
+    recordRepresentation: (record) => {
+        return `${record.name}`
+    }
+};
diff --git a/rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx b/rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx
index c343e76..e950bb9 100644
--- a/rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx
+++ b/rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx
@@ -61,8 +61,8 @@
                     defaultValues={{}}
                 // validate={(values) => { }}
                 >
-                    <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
-                        <Grid item xs={12} md={8}>
+                    <Grid container width={{ xs: '100%', xl: '90%' }} rowSpacing={3} columnSpacing={3}>
+                        <Grid item xs={16} md={10}>
                             <Typography variant="h6" gutterBottom>
                                 {translate('common.edit.title.main')}
                             </Typography>
@@ -109,9 +109,8 @@
                                     parse={v => v}
                                 />
                             </Stack>
-
                         </Grid>
-                        <Grid item xs={12} md={4}>
+                        <Grid item xs={8} md={2}>
                             <Typography variant="h6" gutterBottom>
                                 {translate('common.edit.title.common')}
                             </Typography>
diff --git a/rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx b/rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx
index e2bfea6..a4a436d 100644
--- a/rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx
+++ b/rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx
@@ -142,7 +142,7 @@
               setSelect(record)
               setEditDialog(true)
             }}
-            omit={["id", "createTime", "createBy", "memo"]}
+            omit={["id", "ruleId", "createTime", "createBy", "memo"]}
           >
             <NumberField source="id" />
             <NumberField
@@ -196,7 +196,7 @@
             />
             <WrapperField cellClassName="opt" label="common.field.opt">
               <Button onClick={() => {
-                setSelect(record)
+                // setSelect(record)
                 setEditDialog(true)
               }} label={'ra.action.edit'}
               > </Button>
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java
new file mode 100644
index 0000000..80939f3
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java
@@ -0,0 +1,185 @@
+package com.vincent.rsf.server.manager.controller;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.framework.common.Cools;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.server.common.annotation.OperationLog;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.KeyValVo;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
+import com.vincent.rsf.server.manager.entity.AsnOrderItem;
+import com.vincent.rsf.server.manager.entity.Companys;
+import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
+import com.vincent.rsf.server.manager.enums.CompanysType;
+import com.vincent.rsf.server.manager.service.AsnOrderItemService;
+import com.vincent.rsf.server.manager.service.CompanysService;
+import com.vincent.rsf.server.system.controller.BaseController;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+
+@Api(tags = "鍑哄簱鍗曟槑缁�")
+@RestController
+public class OutOrderItemController extends BaseController {
+
+    @Autowired
+    private AsnOrderItemService asnOrderItemService;
+
+    @Autowired
+    private CompanysService companysService;
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @ApiOperation("鍒嗛〉鑾峰彇鍒楄〃")
+    @PostMapping("/outOrderItem/page")
+    public R page(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<AsnOrderItem, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrderItem.class);
+        return R.ok().add(asnOrderItemService.listByAsnId(pageParam, pageParam.buildWrapper(true)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @PostMapping("/outOrderItem/list")
+    public R list(@RequestBody Map<String, Object> map) {
+        return R.ok().add(asnOrderItemService.list());
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @PostMapping({"/outOrderItem/many/{ids}", "/outOrderItems/many/{ids}"})
+    public R many(@PathVariable Long[] ids) {
+        return R.ok().add(asnOrderItemService.listByIds(Arrays.asList(ids)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @GetMapping("/outOrderItem/{id}")
+    public R get(@PathVariable("id") Long id) {
+        return R.ok().add(asnOrderItemService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:save')")
+    @OperationLog("Create 鍑哄簱鍗曟槑缁�")
+    @PostMapping("/outOrderItem/save")
+    public R save(@RequestBody Map<String, Object> params) {
+        if (Objects.isNull(params)) {
+            throw new CoolException("淇℃伅涓嶈兘涓虹┖锛侊紒");
+        }
+        params.put("createBy", getLoginUserId());
+        params.put("updateBy", getLoginUserId());
+
+        if (!asnOrderItemService.fieldsSave(params)) {
+            return R.error("Save Fail");
+        }
+        return R.ok("Save Success");
+    }
+
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
+    @OperationLog("Update 鍑哄簱鍗曟槑缁�")
+    @PostMapping("/outOrderItem/update")
+    public R update(@RequestBody AsnOrderItem asnOrderItem) {
+        asnOrderItem.setUpdateBy(getLoginUserId());
+        asnOrderItem.setUpdateTime(new Date());
+        if (!Objects.isNull(asnOrderItem.getSplrName()) && StringUtils.isNotBlank(asnOrderItem.getSplrName())) {
+            Companys companys = companysService.getOne(new LambdaQueryWrapper<Companys>()
+                            .eq(Companys::getType, CompanysType.COMPANYS_TYPE_SUPPLIER.val)
+                    .eq(Companys::getId, asnOrderItem.getSplrName()));
+            if (!Objects.isNull(companys)) {
+                asnOrderItem.setSplrCode(companys.getCode()).setSplrName(companys.getName());
+            }
+        }
+        if (!asnOrderItemService.updateById(asnOrderItem)) {
+            return R.error("Update Fail");
+        }
+        return R.ok("Update Success").add(asnOrderItem);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:remove')")
+    @OperationLog("Delete 鍑哄簱鍗曟槑缁�")
+    @PostMapping("/outOrderItem/remove/{ids}")
+    public R remove(@PathVariable Long[] ids) {
+        if (!asnOrderItemService.removeByIds(Arrays.asList(ids))) {
+            return R.error("Delete Fail");
+        }
+        return R.ok("Delete Success").add(ids);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @PostMapping("/outOrderItem/query")
+    public R query(@RequestParam(required = false) String condition) {
+        List<KeyValVo> vos = new ArrayList<>();
+        LambdaQueryWrapper<AsnOrderItem> wrapper = new LambdaQueryWrapper<>();
+        if (!Cools.isEmpty(condition)) {
+            wrapper.like(AsnOrderItem::getId, condition);
+        }
+        asnOrderItemService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
+                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
+        );
+        return R.ok().add(vos);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
+    @PostMapping("/outOrderItem/export")
+    @ApiOperation("瀵煎嚭鍑哄簱鍗曟槑缁�")
+    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
+        List<AsnOrderItem> orderItems = new ArrayList<>();
+        if (!Objects.isNull(map.get("ids"))) {
+            List<Long> ids = JSONArray.parseArray(JSONObject.toJSONString(map.get("ids")), Long.class);
+            if (!ids.isEmpty()) {
+                orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
+                        .in(AsnOrderItem::getId, ids)
+                        .eq(AsnOrderItem::getStatus, 1));
+            } else {
+                orderItems = asnOrderItemService.list(new LambdaQueryWrapper<>());
+            }
+        } else {
+            orderItems = asnOrderItemService.list(new LambdaQueryWrapper<>());
+        }
+
+        ExcelUtil.build(ExcelUtil.create(orderItems, AsnOrderItem.class, true), response);
+    }
+
+    /**
+     * ASN鍗曟嵁鏄庣粏瀵煎叆
+     * @param file
+     * @return
+     */
+    @PostMapping("/outOrderItem/import")
+    @ApiOperation("ASN瀵煎叆鎺ュ彛")
+    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
+    public R importExcel(@RequestParam(value = "file") MultipartFile file, @RequestParam String asnId) throws Exception {
+        if (Objects.isNull(file)) {
+            R.error("鏂囦欢涓嶈兘涓虹┖锛侊紒");
+        }
+        HashMap<String, Object> hashMap = new HashMap<>();
+        return asnOrderItemService.excelImport(file, hashMap, getLoginUserId());
+    }
+
+    /**
+     * @author Ryan
+     * @description 涓嬭浇妯℃澘
+     * @param
+     * @return
+     * @time 2025/4/18 08:17
+     */
+    @PostMapping("/outOrderItem/template/download")
+    @ApiOperation("涓嬭浇鏀惰揣鍗曟ā鏉�")
+    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
+    public void downloadTemplate(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
+        AsnOrderTemplate template = ExcelUtil.mockData(AsnOrderTemplate.class);
+        List<AsnOrderTemplate> list = Arrays.asList(template);
+        ExcelUtil.build(ExcelUtil.create(list, AsnOrderTemplate.class, true), response);
+    }
+
+}
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
new file mode 100644
index 0000000..73618e0
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -0,0 +1,174 @@
+package com.vincent.rsf.server.manager.controller;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.framework.common.Cools;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.server.api.entity.enums.OrderType;
+import com.vincent.rsf.server.api.entity.enums.OrderWorkType;
+import com.vincent.rsf.server.common.annotation.OperationLog;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.KeyValVo;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
+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;
+import com.vincent.rsf.server.manager.enums.AsnExceStatus;
+import com.vincent.rsf.server.manager.service.AsnOrderItemService;
+import com.vincent.rsf.server.manager.service.AsnOrderService;
+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;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+
+@RestController
+@Api(tags = "鍑哄簱鍗曟嵁")
+public class OutStockController extends BaseController {
+
+    @Autowired
+    private AsnOrderService asnOrderService;
+    @Autowired
+    private AsnOrderItemService asnOrderItemService;
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/outStock/page")
+    public R page(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<AsnOrder, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrder.class);
+        return R.ok().add(asnOrderService.page(pageParam, pageParam.buildWrapper(true)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/outStock/list")
+    public R list(@RequestBody Map<String, Object> map) {
+        return R.ok().add(asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getType, OrderType.ORDER_OUT.type)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping({"/outStock/many/{ids}", "/asnOrders/many/{ids}"})
+    public R many(@PathVariable Long[] ids) {
+        return R.ok().add(asnOrderService.listByIds(Arrays.asList(ids)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:asnOrder:list')")
+    @OperationLog("琛ㄥ崟鏌ヨ")
+    @GetMapping("/outStock/{id}")
+    public R get(@PathVariable("id") Long id) {
+        return R.ok().add(asnOrderService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:save')")
+    @OperationLog("Create 鍑哄簱鍗曟嵁")
+    @PostMapping("/outStock/save")
+    @ApiOperation("淇濆瓨")
+    public R save(@RequestBody AsnOrder asnOrder) {
+        asnOrder.setCreateBy(getLoginUserId())
+                .setUpdateBy(getLoginUserId());
+        if (!Objects.isNull(asnOrder.getCode())) {
+            String code = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, asnOrder);
+            asnOrder.setCode(code);
+        }
+        if (!asnOrderService.save(asnOrder)) {
+            return R.error("Save Fail");
+        }
+        return R.ok("Save Success").add(asnOrder);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @OperationLog("Update 锛涘嚭搴撳崟鎹�")
+    @PostMapping("/outStock/update")
+    @ApiOperation("鏇存柊")
+    public R update(@RequestBody AsnOrder asnOrder) {
+        asnOrder.setType(OrderType.ORDER_OUT.type)
+                .setUpdateBy(getLoginUserId())
+                .setUpdateTime(new Date());
+        if (!asnOrderService.updateById(asnOrder)) {
+            return R.error("Update Fail");
+        }
+        return R.ok("Update Success").add(asnOrder);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:remove')")
+    @OperationLog("Delete 鍑哄簱鍗曟嵁")
+    @PostMapping("/outStock/remove/{ids}")
+    public R remove(@PathVariable Long[] ids) {
+        if (!asnOrderService.removeByIds(Arrays.asList(ids))) {
+            return R.error("Delete Fail");
+        }
+        return R.ok("Delete Success").add(ids);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/outStock/query")
+    @ApiOperation("鏌ヨ")
+    public R query(@RequestParam(required = false) String condition) {
+        List<KeyValVo> vos = new ArrayList<>();
+        LambdaQueryWrapper<AsnOrder> wrapper = new LambdaQueryWrapper<>();
+        if (!Cools.isEmpty(condition)) {
+            wrapper.like(AsnOrder::getCode, condition);
+        }
+        asnOrderService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
+                item -> vos.add(new KeyValVo(item.getId(), item.getCode()))
+        );
+        return R.ok().add(vos);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/outStock/export")
+    @ApiOperation("瀵煎嚭")
+    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
+        List<AsnOrder> orders = new ArrayList<>();
+        if (!Objects.isNull(map.get("ids"))) {
+            List<Long> ids = JSONArray.parseArray(JSONObject.toJSONString(map.get("ids")), Long.class);
+            if (!ids.isEmpty()) {
+                orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, ids));
+            } else {
+                orders = asnOrderService.list(new LambdaQueryWrapper<>());
+            }
+        } else {
+            orders = asnOrderService.list();
+        }
+        List<AsnOrderTemplate> orderTemplates = new ArrayList<>();
+        for (AsnOrder order : orders) {
+            List<AsnOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()));
+            for (AsnOrderItem item : orderItems) {
+                if (Objects.isNull(item)) {
+                    continue;
+                }
+                AsnOrderTemplate template = new AsnOrderTemplate();
+                template.setCode(order.getCode())
+                        .setType(OrderType.getValType(order.getType()))
+                        .setWkType(OrderWorkType.getWorkDesc(order.getWkType()))
+                        .setExceStatus(AsnExceStatus.getExceStatus(order.getExceStatus()))
+                        .setAnfme(item.getAnfme() + "")
+                        .setMaktx(item.getMaktx())
+                        .setMemo(item.getMemo())
+                        .setMatnrCode(item.getMatnrCode())
+                        .setPoCode(item.getPoCode())
+                        .setSplrName(item.getSplrName())
+                        .setPoId(order.getPoId() + "")
+                        .setTrackCode(item.getTrackCode())
+                        .setBarcode(item.getBarcode())
+                        .setPackName(item.getPackName())
+                        .setPlatItemId(item.getPlatItemId())
+                        .setSplrBatch(item.getSplrBatch())
+                        .setSplrCode(item.getSplrCode())
+                        .setStockUnit(item.getStockUnit())
+                        .setPurQty(item.getPurQty() + "")
+                        .setPurUnit(item.getPurUnit());
+                orderTemplates.add(template);
+            }
+        }
+        ExcelUtil.build(ExcelUtil.create(orderTemplates, AsnOrderTemplate.class), response);
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
index 9d2adf7..d0ad509 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
@@ -63,4 +63,10 @@
      * DO鍗曟嵁缂栫爜瑙勫垯
      */
     public final static String SYS_DELIVERY_RULE_CODE = "sys_delivery_rule_code";
+
+    /**
+     * 鍑哄簱鍗曟嵁鍙�
+     */
+    public final static String SYS_OUT_STOCK_CODE = "sys_out_stock_code";
+
 }

--
Gitblit v1.9.1