skyouc
3 天以前 27f59f44345d044967e2048f09dbd704f90ce3db
#新增
1. 新增波次功能
2. 库区新增优化
5个文件已添加
4 文件已重命名
1个文件已删除
21个文件已修改
1572 ■■■■ 已修改文件
rsf-admin/src/i18n/en.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/ResourceContent.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveCreate.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveEdit.jsx 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemCreate.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemEdit.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemList.jsx 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveList.jsx 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WavePanel.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/index.jsx 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasEdit.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/WaveEdit.jsx 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItem.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeliveryItem.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WarehouseAreas.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/GroupMergeUtil.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -177,6 +177,8 @@
        inStockPoces: 'In Stock Pocess',
        outStockPoces: 'Out Stock Pocess',
        deviceBind: 'Device Bind',
        wave: 'Wave Manage',
    },
    table: {
        field: {
@@ -720,6 +722,30 @@
                qty: "qty",
                batch: "batch",
            },
            wave: {
                code: "code",
                type: "type",
                exceStatus: "exceStatus",
                anfme: "anfme",
                qty: "qty",
                orderNum: "orderNum",
            },
            waveItem: {
                waveId: "waveId",
                waveCode: "waveCode",
                matnrId: "matnrId",
                matnrName: "matnrName",
                matnrCode: "matnrCode",
                batch: "batch",
                splrBatch: "splrBatch",
                orderCode: "orderCode",
                orderItemId: "orderItemId",
                unit: "unit",
                trackCode: "trackCode",
                fieldsIndex: "fieldsIndex",
                anfme: "anfme",
                workQty: "workQty",
            },
            task: {
                taskCode: "TaskCode",
                taskStatus: "Status",
rsf-admin/src/i18n/zh.js
@@ -178,6 +178,8 @@
        inStockPoces: '入库流程',
        outStockPoces: '出库流程',
        deviceBind: '设备绑定',
        wave: '波次管理',
    },
    table: {
        field: {
@@ -333,6 +335,7 @@
            warehouseAreas: {
                uuid: "唯一编码",
                name: "库区名称",
                type: "库区类型",
                wareId: "所属仓库",
                code: "库区编码",
                shipperId: "货主",
@@ -765,6 +768,30 @@
                qty: "已完成",
                batch: "批次",
            },
            wave: {
                code: "波次号",
                type: "单据类型",
                exceStatus: "状态",
                anfme: "数量",
                qty: "完成数量",
                orderNum: "单据数",
            },
            waveItem: {
                waveId: "波次ID",
                waveCode: "波次号",
                matnrId: "物料ID",
                matnrName: "物料名称",
                matnrCode: "物料编码",
                batch: "批次",
                splrBatch: "供应商批次",
                orderCode: "源单号",
                orderItemId: "源单明细ID",
                unit: "单位",
                trackCode: "跟踪码",
                fieldsIndex: "动态扩展",
                anfme: "数量",
                workQty: "执行数",
            },
            task: {
                taskCode: "任务号",
                taskStatus: "状态",
rsf-admin/src/page/ResourceContent.js
@@ -44,6 +44,7 @@
import delivery from './orders/delivery';
import outStock from './orders/outStock';
import deviceBind from './deviceBind';
import wave from './orders/wave';
const ResourceContent = (node) => {
@@ -128,6 +129,9 @@
            return outStock;
        case 'deviceBind':
            return deviceBind;
        case 'wave':
            return wave;
        default:
            return {
                list: ListGuesser,
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -151,12 +151,7 @@
          sx={{ width: '100%' }}
          preferenceKey='outStock'
          bulkActionButtons={
            <>
              <PublicTaskButton />
              <MyExportButton />
              <BulkDeleteButton mutationMode={OPERATE_MODE}
              />
            </>}
            <PublicTaskButton />}
          rowClick={false}
          expandSingle={true}
          omit={['id', 'createTime', 'createBy', 'memo', 'poId', 'rleStatus$']}
@@ -179,8 +174,7 @@
          <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} /> */}
            <CancelButton></CancelButton>
            <CancelButton />
          </WrapperField>
        </StyledDatagrid>
      </List>
@@ -207,11 +201,21 @@
const PublicTaskButton = () => {
  const record = useRecordContext();
  const { selectedIds, onUnselectItems } = useListContext();
  const notify = useNotify();
  const refresh = useRefresh();
  const redirect = useRedirect();
  const pubClick = (event) => {
  const pubClick = async (event) => {
    event.stopPropagation();
    onUnselectItems();
    const res = await request.post(`/outStock/generate/wave`, { ids: selectedIds });
    if (res?.data?.code === 200) {
      notify(res.data.msg);
      redirect("/wave")
    } else {
      notify(res.data.msg);
    }
    refresh();
  }
  return (
@@ -248,16 +252,8 @@
  const refresh = useRefresh();
  const createByOrder = async (event) => {
    const {selectedIds, onUnselectItems} = useListContext();
    event.stopPropagation();
    setCreateDialog(true);
    const res = await request.post(`/outStock/generate/wave`, { ids: selectedIds });
    if (res?.data?.code === 200) {
      notify(res.data.msg);
    } else {
      notify(res.data.msg);
    }
  }
  return (
rsf-admin/src/page/orders/wave/WaveCreate.jsx
File was renamed from rsf-admin/src/page/wave/WaveCreate.jsx
@@ -27,9 +27,9 @@
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
import DialogCloseButton from "../../components/DialogCloseButton";
import StatusSelectInput from "../../components/StatusSelectInput";
import MemoInput from "../../components/MemoInput";
const WaveCreate = (props) => {
    const { open, setOpen } = props;
rsf-admin/src/page/orders/wave/WaveEdit.jsx
New file
@@ -0,0 +1,145 @@
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,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../../components/EditBaseAside";
import CustomerTopToolBar from "../../components/EditTopToolBar";
import MemoInput from "../../components/MemoInput";
import StatusSelectInput from "../../components/StatusSelectInput";
import WaveItemList from "./WaveItemList";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveEdit = () => {
    const translate = useTranslate();
    return (
        <Box>
            <Edit
                redirect="list"
                mutationMode={EDIT_MODE}
                actions={<CustomerTopToolBar />}
                aside={<EditBaseAside />}
            >
                <SimpleForm
                    shouldUnregister
                    warnWhenUnsavedChanges
                    toolbar={<FormToolbar />}
                    mode="onTouched"
                    defaultValues={{}}
                // validate={(values) => { }}
                >
                    <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}>
                        <Grid item xs={16} md={10} 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',
                            }
                        }}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.main')}
                            </Typography>
                            <Stack direction='row' gap={2}>
                                <TextInput
                                    label="table.field.wave.code"
                                    source="code"
                                    parse={v => v}
                                    readOnly
                                />
                                <SelectInput
                                    label="table.field.wave.type"
                                    source="type"
                                    readOnly
                                    choices={[
                                        { id: 0, name: '手动' },
                                        { id: 1, name: '自动' },
                                    ]}
                                />
                                <SelectInput
                                    label="table.field.wave.exceStatus"
                                    source="exceStatus"
                                    readOnly
                                    choices={[
                                        { id: 0, name: '初始化' },
                                        { id: 1, name: '生成任务' },
                                        { id: 2, name: '任务播种' },
                                        { id: 3, name: '完成' },
                                    ]}
                                />
                            </Stack>
                            <Stack direction='row' gap={2}>
                                <NumberInput
                                    label="table.field.wave.anfme"
                                    readOnly
                                    source="anfme"
                                />
                                <NumberInput
                                    label="table.field.wave.qty"
                                    readOnly
                                    source="qty"
                                />
                                <NumberInput
                                    label="table.field.wave.orderNum"
                                    readOnly
                                    source="orderNum"
                                />
                            </Stack>
                        </Grid>
                        <Grid item xs={8} md={2}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.common')}
                            </Typography>
                            <StatusSelectInput />
                            <Box mt="2em" />
                            <MemoInput />
                        </Grid>
                    </Grid>
                </SimpleForm>
            </Edit >
            <WaveItemList />
        </Box>
    )
}
export default WaveEdit;
rsf-admin/src/page/orders/wave/WaveItemCreate.jsx
New file
@@ -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 WaveItemCreate = (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}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </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 WaveItemCreate;
rsf-admin/src/page/orders/wave/WaveItemEdit.jsx
New file
@@ -0,0 +1,196 @@
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,
    useRecordContext,
    useGetOne,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
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: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveItemEdit = (props) => {
    const { open, setOpen, record } = props;
    const translate = useTranslate();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const { data, isPending, } = useGetOne('waveItem', { 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="waveItem"
                    mutationMode={EDIT_MODE}
                    actions={<CustomerTopToolBar />}
                >
                    <Form
                        shouldUnregister
                        warnWhenUnsavedChanges
                        mode="onTouched"
                        defaultValues={{}}
                    >
                        <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}>
                            <Grid item xs={16} md={10}>
                                <Typography variant="h6" gutterBottom>
                                    {translate('common.edit.title.main')}
                                </Typography>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </Stack>
                            </Grid>
                            <Grid item xs={8} md={2}>
                                <Typography variant="h6" gutterBottom>
                                    {translate('common.edit.title.common')}
                                </Typography>
                                <StatusSelectInput />
                                <Box mt="2em" />
                                <MemoInput />
                            </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 WaveItemEdit;
rsf-admin/src/page/orders/wave/WaveItemList.jsx
New file
@@ -0,0 +1,190 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } 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,
    useGetRecordId,
    Button,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveItemCreate from "./WaveItemCreate";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import PageDrawer from "../../components/PageDrawer";
import WaveItemEdit from "./WaveItemEdit";
import MyField from "../../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import ContentCreate from '@mui/icons-material/Create';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="waveId" label="table.field.waveItem.waveId" />,
    <TextInput source="waveCode" label="table.field.waveItem.waveCode" />,
    <NumberInput source="matnrId" label="table.field.waveItem.matnrId" />,
    <TextInput source="maktx" label="table.field.waveItem.matnrName" />,
    <TextInput source="matnrCode" label="table.field.waveItem.matnrCode" />,
    <TextInput source="batch" label="table.field.waveItem.batch" />,
    <TextInput source="splrBatch" label="table.field.waveItem.splrBatch" />,
    <TextInput source="orderCode" label="table.field.waveItem.orderCode" />,
    <NumberInput source="orderItemId" label="table.field.waveItem.orderItemId" />,
    <TextInput source="unit" label="table.field.waveItem.unit" />,
    <TextInput source="trackCode" label="table.field.waveItem.trackCode" />,
    <TextInput source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />,
    <NumberInput source="anfme" label="table.field.waveItem.anfme" />,
    <NumberInput source="workQty" label="table.field.waveItem.workQty" />,
    <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 WaveItemList = () => {
    const translate = useTranslate();
    const waveId = useGetRecordId();
    const [createDialog, setCreateDialog] = useState(false);
    const [editDialog, setEditDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                resource="waveItem"
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.waveItem"}
                empty={false}
                filters={filters}
                filter={{ waveId: waveId }}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <SelectColumnsButton preferenceKey='waveItem' />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='waveItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={false}
                    expandSingle={false}
                    omit={['id', 'createTime', 'matnrId', 'waveId', 'batch', 'orderItemId', 'batch', 'fieldsIndex', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="waveId" label="table.field.waveItem.waveId" />
                    <TextField source="waveCode" label="table.field.waveItem.waveCode" />
                    <NumberField source="matnrId" label="table.field.waveItem.matnrId" />
                    <TextField source="maktx" label="table.field.waveItem.matnrName" />
                    <TextField source="matnrCode" label="table.field.waveItem.matnrCode" />
                    <TextField source="batch" label="table.field.waveItem.batch" />
                    <TextField source="splrBatch" label="table.field.waveItem.splrBatch" />
                    <TextField source="orderCode" label="table.field.waveItem.orderCode" />
                    <NumberField source="orderItemId" label="table.field.waveItem.orderItemId" />
                    <TextField source="unit" label="table.field.waveItem.unit" />
                    <TextField source="trackCode" label="table.field.waveItem.trackCode" />
                    <TextField source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />
                    <NumberField source="anfme" label="table.field.waveItem.anfme" />
                    <NumberField source="workQty" label="table.field.waveItem.workQty" />
                    <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="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                </StyledDatagrid>
            </List>
            <WaveItemEdit
                open={editDialog}
                setOpen={setEditDialog}
            />
            <WaveItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='WaveItem Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveItemList;
const DetailButton = (setEditDialog) => {
    const record = useRecordContext();
    const editClick = (event) => {
        console.log('--========--->');
        event.stopPropagation()
        setEditDialog(true)
    }
    return (
        <Button  label="ra.action.edit" onClick={editClick} startIcon={<ContentCreate />}/>
    )
}
rsf-admin/src/page/orders/wave/WaveList.jsx
File was renamed from rsf-admin/src/page/wave/WaveList.jsx
@@ -31,18 +31,24 @@
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useRefresh,
    useRedirect,
    Button,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveCreate from "./WaveCreate";
import WavePanel from "./WavePanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import PageDrawer from "../../components/PageDrawer";
import MyField from "../../components/MyField";
import request from '@/utils/request';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import PublicIcon from '@mui/icons-material/Public';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -96,7 +102,6 @@
const WaveList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
@@ -112,26 +117,26 @@
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.wave"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                empty={false}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='wave' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='wave'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    bulkActionButtons={
                        <PublicTaskButton />
                    }
                    rowClick={(id, resource, record) => false}
                    expand={() => <WavePanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                    expand={false}
                    expandSingle={false}
                    omit={['id', 'createTime', 'createBy', 'memo', 'createBy$']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.wave.code" />
@@ -140,14 +145,9 @@
                    <NumberField source="anfme" label="table.field.wave.anfme" />
                    <NumberField source="qty" label="table.field.wave.qty" />
                    <NumberField source="orderNum" label="table.field.wave.orderNum" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <TextField source="updateBy$" label="common.field.updateBy" />
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <TextField source="createBy$" label="common.field.createBy" />
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
@@ -172,3 +172,33 @@
}
export default WaveList;
const PublicTaskButton = () => {
    const record = useRecordContext();
    const { selectedIds, onUnselectItems } = useListContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const redirect = useRedirect();
    const pubClick = async (event) => {
        event.stopPropagation();
        console.log('=========>');
        onUnselectItems();
        const res = await request.post(`/wave/public/task`, { ids: selectedIds });
        if (res?.data?.code === 200) {
            notify(res.data.msg);
            redirect("/task")
        } else {
            notify(res.data.msg);
        }
        refresh();
    }
    return (
        <Button
            onClick={pubClick}
            label={"toolbar.createTask"}
            startIcon={<PublicIcon />}
        />);
}
rsf-admin/src/page/orders/wave/WavePanel.jsx
File was renamed from rsf-admin/src/page/wave/WavePanel.jsx
@@ -4,7 +4,7 @@
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import PanelTypography from "../../components/PanelTypography";
import * as Common from '@/utils/common'
const WavePanel = () => {
rsf-admin/src/page/orders/wave/index.jsx
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx
@@ -36,7 +36,7 @@
  const translate = useTranslate();
  const notify = useNotify();
  const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
@@ -91,32 +91,29 @@
            </DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
              <Grid container rowSpacing={2} columnSpacing={2}>
                {/* <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.uuid"
                    source="uuid"
                    parse={(v) => v}
                    validate={[required()]}
                    autoFocus
                  />
                </Grid> */}
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.name"
                    source="name"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.code"
                    source="code"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                  <AutocompleteInput
                    choices={dicts}
                    optionText="label"
                    label="table.field.asnOrder.type"
                    source="type"
                    optionValue="value"
                    parse={v => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <ReferenceInput
                    source="warehouseId"
                    reference="warehouse"
@@ -128,8 +125,7 @@
                      filterToQuery={(val) => ({ name: val })}
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <ReferenceInput source="shipperId" reference="companys" filter={{ type: 'shipper' }}>
                    <AutocompleteInput
                      label="table.field.warehouseAreas.shipperId"
@@ -137,8 +133,6 @@
                      filterToQuery={(val) => ({ name: val })}
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <ReferenceInput source="supplierId" reference="companys" filter={{ type: 'supplier' }}>
                    <AutocompleteInput
                      label="table.field.warehouseAreas.supplierId"
@@ -147,7 +141,7 @@
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMinus"
                    source="flagMinus"
@@ -157,8 +151,6 @@
                      { id: 1, name: "是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagLabelMange"
                    source="flagLabelMange"
@@ -168,8 +160,6 @@
                      { id: 1, name: " 是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMix"
                    source="flagMix"
rsf-admin/src/page/warehouseAreas/WarehouseAreasEdit.jsx
@@ -42,6 +42,7 @@
const WarehouseAreasEdit = () => {
    const translate = useTranslate();
    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
    return (
        <Edit
@@ -58,8 +59,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: '100%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={16} md={10}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
@@ -72,23 +73,27 @@
                                autoFocus
                            />
                        </Stack> */}
                        <Stack direction='row' gap={2}>
                        <Stack xs={16} direction='row' gap={2}>
                            <TextInput
                                label="table.field.warehouseAreas.name"
                                source="name"
                                validate={[required()]}
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.warehouseAreas.code"
                                source="code"
                                validate={[required()]}
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <AutocompleteInput
                                choices={dicts}
                                optionText="label"
                                label="table.field.asnOrder.type"
                                source="type"
                                optionValue="value"
                                parse={v => v}
                            />
                            <ReferenceInput
                                source="warehouseId"
                                reference="warehouse"
@@ -113,8 +118,6 @@
                                    filterToQuery={(val) => ({ name: val })}
                                />
                            </ReferenceInput>
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <ReferenceInput
                                source="supplierId"
                                reference="companys"
@@ -126,8 +129,6 @@
                                    filterToQuery={(val) => ({ name: val })}
                                />
                            </ReferenceInput>
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagMinus"
                                source="flagMinus"
@@ -137,8 +138,6 @@
                                    { id: 1, name: '是' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagLabelMange"
                                source="flagLabelMange"
@@ -148,8 +147,6 @@
                                    { id: 1, name: ' 是' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagMix"
                                source="flagMix"
@@ -160,9 +157,8 @@
                                ]}
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                    <Grid item xs={8} md={2}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
@@ -114,9 +114,9 @@
const WarehouseAreasList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
    return (
        <Box display="flex">
@@ -160,6 +160,7 @@
                    {/* <TextField source="uuid" label="table.field.warehouseAreas.uuid" /> */}
                    <TextField source="name" label="table.field.warehouseAreas.name" />
                    <TextField source="code" label="table.field.warehouseAreas.code" />
                    <TextField source="type$" label="table.field.warehouseAreas.type"/>
                    {/* <ReferenceField source="shipperId" label="table.field.warehouseAreas.shipperId" reference="shipper" link={false} sortable={false}>
                        <TextField source="name" />
                    </ReferenceField> */}
@@ -169,7 +170,6 @@
                    <TextField source="flagMinus$" label="table.field.warehouseAreas.flagMinus" sortable={false} />
                    <TextField source="flagLabelMange$" label="table.field.warehouseAreas.flagLabelMange" sortable={false} />
                    <TextField source="flagMix$" label="table.field.warehouseAreas.flagMix" sortable={false} />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
rsf-admin/src/page/wave/WaveEdit.jsx
File was deleted
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
@@ -15,16 +15,16 @@
        generator.frontendPrefixPath = "rsf-admin/";
        generator.sqlOsType = SqlOsType.MYSQL;
        generator.url="127.0.0.1:3306/rsf";
        generator.username="root";
        generator.password="34821015";
        generator.url = "127.0.0.1:3306/rsf";
        generator.username = "root";
        generator.password = "34821015";
//        generator.url="47.97.1.152:51433;databasename=jkasrs";
//        generator.username="sa";
//        generator.password="Zoneyung@zy56$";
        generator.table="man_device_bind";
        generator.tableDesc="立体库站点绑定";
        generator.packagePath="com.vincent.rsf.server.manager";
        generator.table = "man_device_bind";
        generator.tableDesc = "立体库站点绑定";
        generator.packagePath = "com.vincent.rsf.server.manager";
        generator.build();
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java
@@ -55,10 +55,10 @@
    @OperationLog("Create 立体库站点绑定")
    @PostMapping("/deviceBind/save")
    public R save(@RequestBody DeviceBind deviceBind) {
        deviceBind.setCreateBy(getLoginUserId());
        deviceBind.setCreateTime(new Date());
        deviceBind.setUpdateBy(getLoginUserId());
        deviceBind.setUpdateTime(new Date());
//        deviceBind.setCreateBy(getLoginUserId());
//        deviceBind.setCreateTime(new Date());
//        deviceBind.setUpdateBy(getLoginUserId());
//        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.save(deviceBind)) {
            return R.error("Save Fail");
        }
@@ -69,8 +69,8 @@
    @OperationLog("Update 立体库站点绑定")
    @PostMapping("/deviceBind/update")
    public R update(@RequestBody DeviceBind deviceBind) {
        deviceBind.setUpdateBy(getLoginUserId());
        deviceBind.setUpdateTime(new Date());
//        deviceBind.setUpdateBy(getLoginUserId());
//        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.updateById(deviceBind)) {
            return R.error("Update Fail");
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -80,7 +80,8 @@
            if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
                return R.error("编码规则错误:编码「SYS_OUT_STOCK_CODE」是未设置成功!!");
            }
            asnOrder.setCode(ruleCode);
            asnOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                    .setCode(ruleCode);
        }
        if (!outStockService.save(asnOrder)) {
            return R.error("Save Fail");
@@ -213,6 +214,4 @@
        List<Long> ids = (List<Long>) params.get("ids");
        return outStockService.generateWaves(ids);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java
@@ -4,6 +4,7 @@
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.utils.ExcelUtil;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
@@ -12,6 +13,7 @@
import com.vincent.rsf.server.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.WaveService;
import com.vincent.rsf.server.system.controller.BaseController;
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.*;
@@ -105,4 +107,15 @@
        ExcelUtil.build(ExcelUtil.create(waveService.list(), Wave.class), response);
    }
    @PreAuthorize("hasAuthority('manager:wave:update')")
    @ApiOperation("波次下发任务")
    @PostMapping("/wave/public/task")
    public R publicTask(@RequestBody Map<String, Object> map) {
        if (Cools.isEmpty(map)) {
            throw new CoolException("参数不能为空!!");
        }
        return waveService.publicTask(map);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItem.java
@@ -55,6 +55,7 @@
    @ApiModelProperty("字段索引")
    private String fieldsIndex;
    @ApiModelProperty("执行数量")
    private Double workQty;
@@ -229,7 +230,7 @@
     */
    @ApiModelProperty(value= "备注")
    private String memo;
//
    public AsnOrderItem() {}
    public AsnOrderItem(Long asnId,String asnCode,Long poDetlId, String matnrCode, String poCode,Long matnrId,String matnk,Double anfme,String stockUnit,Double purQty,String purUnit,Double qty,String splrCode,String splrName,String qrcode,String barcode,String packName,Integer status, Integer ntyStatus,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeliveryItem.java
@@ -59,10 +59,16 @@
    private String matnrCode;
    /**
     * 物料ID
     */
    @ApiModelProperty("物料ID")
    private Long matnrId;
    /**
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String matnrName;
    private String maktx;
    /**
     * 动态字段索引
@@ -118,6 +124,18 @@
    @ApiModelProperty(value= "供应商批次")
    private String splrBatch;
    @ApiModelProperty("批次")
    private String batch;
    @ApiModelProperty("跟踪码")
    private String trackCode;
    @ApiModelProperty("生产日期")
    private Date prodTime;
    @ApiModelProperty("包装")
    private String packName;
    /**
     * 状态 1: 正常  0: 冻结  
     */
@@ -171,11 +189,12 @@
    public DeliveryItem() {}
    public DeliveryItem(Long deliveryId,String platItemId,String matnrCode,String matnrName,String fieldsIndex,String unit,Double anfme,Double qty,Double nromQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
    public DeliveryItem(Long deliveryId,String platItemId, Long matnrId,String matnrCode,String matnrName,String fieldsIndex,String unit,Double anfme,Double qty,Double nromQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.deliveryId = deliveryId;
        this.platItemId = platItemId;
        this.matnrId = matnrId;
        this.matnrCode = matnrCode;
        this.matnrName = matnrName;
        this.maktx = matnrName;
        this.fieldsIndex = fieldsIndex;
        this.unit = unit;
        this.anfme = anfme;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java
@@ -118,16 +118,16 @@
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
//    public Boolean getStatusBool(){
//        if (null == this.status){ return null; }
//        switch (this.status){
//            case 1:
//                return true;
//            case 0:
//                return false;
//            default:
//                return null;
//        }
//    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WarehouseAreas.java
@@ -3,13 +3,18 @@
import com.baomidou.mybatisplus.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.vincent.rsf.server.manager.service.CompanysService;
import com.vincent.rsf.server.manager.service.ShipperService;
import com.vincent.rsf.server.manager.service.WarehouseService;
import com.vincent.rsf.server.system.constant.DictTypeCode;
import com.vincent.rsf.server.system.entity.DictData;
import com.vincent.rsf.server.system.service.DictDataService;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -24,7 +29,7 @@
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Collectors;
@Data
@TableName("man_warehouse_areas")
@@ -48,7 +53,7 @@
    /**
     * 编号
     */
    @ApiModelProperty(value= "编号")
    @ApiModelProperty(value= "业务类型")
    private String type;
    /**
@@ -156,8 +161,8 @@
    public WarehouseAreas() {}
    public WarehouseAreas(String uuid,String name,String code,Long shipperId,Short supplierId,Short flagMinus,Short flagLabelMange,Short flagMix,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.type = uuid;
    public WarehouseAreas(String type,String name,String code,Long shipperId,Short supplierId,Short flagMinus,Short flagLabelMange,Short flagMix,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.type = type;
        this.name = name;
        this.code = code;
        this.shipperId = shipperId;
@@ -194,6 +199,18 @@
        return null;
    }
    public String getType$() {
        if (Cools.isEmpty(this.type)){
            return "";
        }
        DictDataService dictDataService = SpringUtils.getBean(DictDataService.class);
        DictData dictData = dictDataService.getOne(new LambdaQueryWrapper<DictData>().eq(DictData::getDictTypeCode, DictTypeCode.SYS_WARE_AREAS_TYPE).eq(DictData::getValue, this.type));
        if (Objects.isNull(dictData)) {
            return null;
        }
        return dictData.getLabel();
    }
    public String getSupplierId$() {
        CompanysService service = SpringUtils.getBean(CompanysService.class);
        Companys supplier = service.getById(this.supplierId);
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java
@@ -59,7 +59,7 @@
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String matnrName;
    private String maktx;
    /**
     * 商品编号
@@ -180,13 +180,14 @@
    public WaveItem() {}
    public WaveItem(Long waveId,String waveCode,Long matnrId,String matnrName,String matnrCode,String batch,String splrBatch,String orderCode,Long orderItemId,String unit,String trackCode,String fieldsIndex,Double anfme,Double workQty,Long tenantId,Integer status,Integer deleted,Date createTime,Long createBy,Date updateTime,Long updateBy,String memo) {
    public WaveItem(Long waveId,String waveCode,Long matnrId,String matnrName,String matnrCode,String batch,String splrBatch,String orderCode, Long orderId,Long orderItemId,String unit,String trackCode,String fieldsIndex,Double anfme,Double workQty,Long tenantId,Integer status,Integer deleted,Date createTime,Long createBy,Date updateTime,Long updateBy,String memo) {
        this.waveId = waveId;
        this.waveCode = waveCode;
        this.matnrId = matnrId;
        this.matnrName = matnrName;
        this.maktx = matnrName;
        this.matnrCode = matnrCode;
        this.batch = batch;
        this.orderId = orderId;
        this.splrBatch = splrBatch;
        this.orderCode = orderCode;
        this.orderItemId = orderItemId;
@@ -205,6 +206,7 @@
        this.memo = memo;
    }
//    WaveItem waveItem = new WaveItem(
//            null,    // 波次ID
//            null,    // 波次号
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java
@@ -14,8 +14,14 @@
    ASN_EXCE_STATUS_EXCE_ING("1", "执行中"),
    ASN_EXCE_STATUS_TASK_DONE("2", "已完成"),
    ASN_EXCE_STATUS_TASK_CANCEL("3", "取消"),
    ASN_EXCE_STATUS_TASK_CLOSE("4", "已关闭")
            ;
    ASN_EXCE_STATUS_TASK_CLOSE("4", "已关闭"),
    OUT_STOCK_STATUS_TASK_INIT("5", "初始化"),
    OUT_STOCK_STATUS_TASK_EXCE("6", "待处理"),
    OUT_STOCK_STATUS_TASK_WAVE("7", "生成波次"),
    OUT_STOCK_STATUS_TASK_WORKING("8", "作业中")
    ;
    AsnExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
@@ -35,6 +41,10 @@
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CANCEL.desc;
        } else if (val.equals(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val)) {
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.desc;
        } else if (val.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.desc;
        } else if (val.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.desc;
        } else {
            return null;
        }
@@ -51,6 +61,10 @@
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CANCEL.val;
        } else if (desc.equals(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.desc)) {
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val;
        }else if (desc.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.desc)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val;
        } else if (desc.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.desc)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val;
        } else {
            return null;
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java
@@ -1,8 +1,19 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.entity.Wave;
import java.util.Map;
public interface WaveService extends IService<Wave> {
    /**
     * @author Ryan
     * @description 波次任务下发
     * @param
     * @return
     * @time 2025/4/25 16:24
     */
    R publicTask(Map<String, Object> map);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -6,19 +6,14 @@
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.api.entity.dto.PoItemsDto;
import com.vincent.rsf.server.api.service.ReceiveMsgService;
import com.vincent.rsf.server.api.service.ReportMsgService;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam;
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.enums.WaveExceStatus;
import com.vincent.rsf.server.manager.mapper.AsnOrderMapper;
import com.vincent.rsf.server.manager.mapper.PurchaseMapper;
import com.vincent.rsf.server.manager.service.*;
import com.vincent.rsf.server.manager.utils.GroupMergeUtil;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.mapper.SerialRuleMapper;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -26,7 +21,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@@ -208,7 +202,7 @@
            if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
                throw new CoolException("编码规则错误:请检查 「SYS_OUT_STOCK_CODE」编码是否设置成功");
            }
            order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)
            order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                    .setCode(ruleCode)
                    .setPoId(delivery.getId())
                    .setId(null)
@@ -222,6 +216,9 @@
                BeanUtils.copyProperties(item, orderItem);
                orderItem.setId(null)
                        .setPoCode(order.getPoCode())
                        .setMaktx(item.getMaktx())
                        .setMatnrCode(item.getMatnrCode())
                        .setFieldsIndex(item.getFieldsIndex())
                        .setAsnId(order.getId())
                        .setAsnCode(order.getCode())
                        .setPlatItemId(item.getPlatItemId())
@@ -255,9 +252,11 @@
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        List<AsnOrder> orders = this.listByIds(ids);
        List<AsnOrder> orders = this.list(new LambdaQueryWrapper<AsnOrder>()
                .in(AsnOrder::getId, ids)
                .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val));
        if (orders.isEmpty()) {
            throw new CoolException("单据不存在!!");
            throw new CoolException("当前单据状态不能执行波次生成操作!!");
        }
        double sum = orders.stream().mapToDouble(AsnOrder::getAnfme).sum();
        Wave wave = new Wave();
@@ -274,25 +273,83 @@
            throw new CoolException("波次保存失败!!");
        }
        List<Long> list = orders.stream().map(AsnOrder::getId).collect(Collectors.toList());
        List<WaveItem> items = new ArrayList<>();
        List<AsnOrderItem> orderItems = asnOrderItemService
                .list(new LambdaQueryWrapper<AsnOrderItem>()
                        .in(AsnOrderItem::getAsnId, list));
        for (AsnOrderItem item : orderItems) {
            WaveItem waveItem = new WaveItem();
            BeanUtils.copyProperties(item, waveItem);
            waveItem.setWaveId(wave.getId())
                    .setWaveCode(wave.getCode())
                    .setOrderId(item.getAsnId())
                    .setOrderCode(item.getAsnCode())
                    .setOrderItemId(item.getId());
            items.add(waveItem);
        if (orderItems.isEmpty()) {
            throw new CoolException("单据不存在!!");
        }
        if (!waveItemService.saveBatch(items)) {
        List<WaveItem> waveItems = mergeWave(orderItems, wave);
        if (!waveItemService.saveBatch(waveItems)) {
            throw new CoolException("波次明细保存失败!!");
        }
        double sum1 = waveItems.stream().mapToDouble(WaveItem::getAnfme).sum();
        wave.setAnfme(sum1);
        if (!waveService.saveOrUpdate(wave)) {
            throw new CoolException("主单修改失败!!");
        }
        if (!this.update(new LambdaUpdateWrapper<AsnOrder>()
                .set(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WAVE.val)
                .in(AsnOrder::getId, ids))) {
            throw new CoolException("执行状态修改修改失败!!");
        }
        return R.ok("操作完成!!");
    }
    /**
     * @param
     * @param wave
     * @return
     * @author Ryan
     * @description 合并生成波次
     * @time 2025/4/25 10:07
     */
    private List<WaveItem> mergeWave(List<AsnOrderItem> orderItems, Wave wave) {
        List<WaveItem> items = new ArrayList<>();
        orderItems.forEach(order -> {
            WaveItem item = new WaveItem();
            BeanUtils.copyProperties(order, item);
            item.setOrderItemId(order.getId())
                    .setId(null)
                    .setOrderCode(order.getAsnCode())
                    .setOrderId(order.getAsnId())
                    .setMatnrId(order.getMatnrId())
                    .setMaktx(order.getMaktx())
                    .setWaveId(wave.getId())
                    .setWaveCode(wave.getCode());
            items.add(item);
        });
        List<WaveItem> waveItems = GroupMergeUtil.groupAndMerge(items,
                (p1, p2) -> new WaveItem(
                        p1.getWaveId(),
                        p1.getWaveCode(),
                        p1.getMatnrId(),
                        p1.getMaktx(),
                        p1.getMatnrCode(),
                        p1.getBatch(),
                        p1.getSplrBatch(),
                        p1.getOrderCode(),
                        p1.getOrderId(),
                        p1.getOrderItemId(),
                        p1.getUnit(),
                        p1.getTrackCode(),
                        p1.getFieldsIndex(),
                        p1.getAnfme() + p2.getAnfme(),
                        p1.getWorkQty(),
                        p1.getTenantId(),
                        p1.getStatus(),
                        p1.getDeleted(),
                        p1.getCreateTime(),
                        p1.getCreateBy(),
                        p1.getUpdateTime(),
                        p1.getUpdateBy(),
                        p1.getMemo()
                ),
                WaveItem::getSplrBatch, WaveItem::getMatnrCode, WaveItem::getFieldsIndex
        );
        return waveItems;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -1,12 +1,64 @@
package com.vincent.rsf.server.manager.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.manager.entity.AsnOrder;
import com.vincent.rsf.server.manager.entity.AsnOrderItem;
import com.vincent.rsf.server.manager.enums.WaveExceStatus;
import com.vincent.rsf.server.manager.mapper.WaveMapper;
import com.vincent.rsf.server.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.AsnOrderItemService;
import com.vincent.rsf.server.manager.service.AsnOrderService;
import com.vincent.rsf.server.manager.service.WaveService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Service("waveService")
public class WaveServiceImpl extends ServiceImpl<WaveMapper, Wave> implements WaveService {
    @Autowired
    private AsnOrderItemService asnOrderItemService;
    @Autowired
    private AsnOrderService asnOrderService;
    /**
     * @author Ryan
     * @description 波次任务下发
     * @param
     * @return
     * @time 2025/4/25 16:24
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R publicTask(Map<String, Object> map) {
        List<Long> ids = (List<Long>) map.get("ids");
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        List<Wave> waves = this.list(new LambdaQueryWrapper<Wave>().in(Wave::getId, ids));
        if (Objects.isNull(waves) || waves.isEmpty()) {
            throw new CoolException("波次数据不存在!!");
        }
        List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getId, ids));
        asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, ids));
        if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).in(Wave::getId,ids))) {
            throw new CoolException("波次状态修改失败!!");
        }
        return R.ok();
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/GroupMergeUtil.java
New file
@@ -0,0 +1,154 @@
package com.vincent.rsf.server.manager.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import lombok.Data;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * @author Ryan
 * @description 根据对象多个属性合并处理
 * @param
 * @return
 * @time 2025/4/25 10:55
 */
public class GroupMergeUtil {
    /**
     * 根据多个属性分组合并对象列表
     *
     * @param list       待分组的对象列表
     * @param mergers    合并函数,定义如何合并同一组中的对象
     * @param keyGetters 分组属性的getter方法数组
     * @param <T>        对象类型
     * @param <K>        分组键类型
     * @return 分组合并后的对象列表
     */
    @SafeVarargs
    public static <T, K> List<T> groupAndMerge(
            List<T> list,
            BinaryOperator<T> mergers,
            Function<T, K>... keyGetters) {
        return new ArrayList<>(list.stream()
                .collect(Collectors.toMap(
                        item -> new GroupingKeys<>(Arrays.stream(keyGetters)
                                .map(getter -> getter.apply(item))
                                .toArray()),
                        Function.identity(),
                        mergers))
                .values());
    }
    /**
     * @author Ryan
     * @description 用于存储多个分组键的内部类
     * @param
     * @return
     * @time 2025/4/25 10:56
     */
    private static class GroupingKeys<K> {
        private final Object[] keys;
        private final int hashCode;
        public GroupingKeys(Object[] keys) {
            this.keys = keys;
            this.hashCode = Arrays.deepHashCode(keys);
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            GroupingKeys<?> that = (GroupingKeys<?>) o;
            return Arrays.deepEquals(keys, that.keys);
        }
        @Override
        public int hashCode() {
            return hashCode;
        }
    }
    // 示例使用
//    public static void main(String[] args) {
//        // 示例数据类
//        @Data
//        class Product {
//         private String category;
//            private String subCategory;
//            private String abc;
//
//            private String dbc;
//
//            private String brand;
//            private int quantity;
//            private double price;
//
//            public Product(String category, String abc, String dbc, String subCategory, String brand, int quantity, double price) {
//                this.category = category;
//                this.subCategory = subCategory;
//                this.abc = abc;
//                this.dbc = dbc;
//                this.brand = brand;
//                this.quantity = quantity;
//                this.price = price;
//            }
//
//            // getters...
////            public String getCategory() { return category; }
////            public String getSubCategory() { return subCategory; }
////            public String getBrand() { return brand; }
////            public int getQuantity() { return quantity; }
////            public double getPrice() { return price; }
//
//            @Override
//            public String toString() {
//                return String.format("[%s, %s, %s] - Qty: %d, Price: %.2f",
//                        category, subCategory, brand, quantity, price);
//            }
//        }
//
//        // 创建测试数据
//        List<Product> products = Arrays.asList(
//                new Product("Electronics", "Phone", "Apple", 10, 999.99),
//                new Product("Electronics", "Phone", "Samsung", 15, 899.99),
//                new Product("Electronics", "Phone", "Apple", 5, 999.99),
//                new Product("Electronics", "Tablet", "Apple", 8, 799.99),
//                new Product("Clothing", "Shirt", "Nike", 20, 29.99),
//                new Product("Clothing", "Shirt", "Nike", 10, 29.99),
//                new Product("Clothing", "Pants", "Adidas", 12, 49.99)
//        );
//
//        // 按 category, subCategory, brand 分组,合并 quantity 相加,price 取平均值
//        List<Product> mergedProducts = groupAndMerge(
//                products,
//                (p1, p2) -> new Product(
//                        p1.category,
//                        p1.subCategory,
//                        p1.brand,
//                        p1.quantity + p2.quantity,
//                        (p1.price + p2.price) / 2  // 这里简单平均,实际业务可能不同
//                ),
//                Product::getCategory,
//                Product::getSubCategory,
//                Product::getBrand
//        );
//
////        // 打印结果
////        System.out.println("合并后的产品列表:");
////        System.out.println(JSONArray.toJSONString(mergedProducts));
////        mergedProducts.forEach(item -> {
////            System.out.println(JSONObject.toJSONString(item));
////        });
////        mergedProducts.forEach(System.out::println);
//    }
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java
@@ -54,4 +54,9 @@
     */
    public final static String DICT_ASN_EXCE_STATUS = "sys_asn_exce_status";
    /**
     * 仓库库区类型
     */
    public final static String SYS_WARE_AREAS_TYPE = "sys_ware_areas_type";
}