20个文件已修改
1个文件已添加
707 ■■■■ 已修改文件
rsf-admin/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/asnOrder/AsnWareModal.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutStockPublic.jsx 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/McpController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/constant/Constants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/dto/OrderOutItemDto.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/LocToTaskParams.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/OutStockToTaskParams.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 167 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/.env
@@ -1,3 +1,3 @@
VITE_BASE_IP=192.168.4.50
VITE_BASE_IP=127.0.0.1
# VITE_BASE_IP=47.76.147.249
VITE_BASE_PORT=8080
rsf-admin/src/page/orders/asnOrder/AsnWareModal.jsx
@@ -20,11 +20,9 @@
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);
@@ -55,7 +53,6 @@
        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 => {
            console.log(el);
            const dynamicFields = dyFields.reduce((acc, item) => {
                acc[item.fields] = el['extendFields']?.[item.fields] || '';
                return acc;
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -298,6 +298,6 @@
  }
  return (
    <ConfirmButton label={"toolbar.publicWorking"} startIcon={<AddTaskIcon />} onConfirm={taskEvent} size={"small"} />
    record.exceStatus == 10 ? <ConfirmButton label={"toolbar.publicWorking"} startIcon={<AddTaskIcon />} onConfirm={taskEvent} size={"small"} /> : <></>
  )
}
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx
@@ -13,7 +13,6 @@
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    Toolbar,
    useRecordContext,
    useTranslate,
    useNotify,
@@ -45,7 +44,6 @@
import { Grid, margin, Stack, width } from "@mui/system";
import request from '@/utils/request';
import SaveIcon from '@mui/icons-material/Save';
import debounce from "lodash/debounce";
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
rsf-admin/src/page/orders/outStock/OutStockPublic.jsx
@@ -1,4 +1,4 @@
import { Box, Card, Grid, LinearProgress, Select, MenuItem, ListItemText, Tooltip } from "@mui/material";
import { Box, Card, Grid, LinearProgress, Select, MenuItem, ListItemText, Tooltip, IconButton } from "@mui/material";
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    List,
@@ -13,7 +13,6 @@
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    Toolbar,
    useRecordContext,
    useTranslate,
    useNotify,
@@ -36,14 +35,17 @@
    SimpleForm,
    required,
    Form,
    useRefresh,
    useRedirect,
} from 'react-admin';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
import { styled } from '@mui/material/styles';
import { DataGrid, useGridApiContext } from '@mui/x-data-grid';
import { DataGrid, useGridApiContext, GridActionsCellItem, useGridApiRef } from '@mui/x-data-grid';
import request from '@/utils/request';
import ConfirmationNumberOutlinedIcon from '@mui/icons-material/ConfirmationNumberOutlined';
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
import ConfirmButton from '../../components/ConfirmButton';
import { Delete, Edit, Add } from '@mui/icons-material';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -60,8 +62,8 @@
const OutStockPublic = (props) => {
    const { record, open, setOpen } = props;
    const dataGridRef = useRef(null);
    const notify = useNotify();
    const gridRef = useGridApiRef();
    const [rows, setRows] = useState({});
    const translate = useTranslate();
    const [selectedIds, setSelectedIds] = React.useState([]);
@@ -91,11 +93,13 @@
            [name]: value
        }));
    };
    return (
        <>
            <Box>
                <Grid sx={{ display: "flex" }} container rowSpacing={2} columnSpacing={2}>
                    <Grid item xl={6} gap={2} >
                    <Grid item xl={5.7} gap={2} >
                        <Card>
                            <Form>
                                <ReferenceInput
@@ -124,9 +128,7 @@
                                empty={false}
                                filter={{ asnId: record?.id, deleted: 0 }}
                                sort={{ field: "create_time", order: "desc" }}
                                actions={(
                                    <SelectColumnsButton preferenceKey='outStock' />
                                )}
                                actions={false}
                                perPage={DEFAULT_ITEM_PAGE_SIZE}
                            >
                                <LinearProgress
@@ -152,30 +154,14 @@
                            </List>
                        </Card>
                    </Grid>
                    <Grid item xl={6} gap={2}>
                    <Grid item xl={6.3} gap={2}>
                        <Card>
                            <Box>
                                <DataGrid
                                    rows={rows}
                                    columns={columns}
                                    initialState={{
                                        pagination: {
                                            paginationModel: {
                                                pageSize: 15,
                                            },
                                        },
                                    }}
                                    ref={dataGridRef}
                                    checkboxSelection
                                    onRowSelectionModelChange={(ids) => {
                                        setSelectedIds(ids)
                                    }}
                                    pageSizeOptions={[15, 25, 35, 45]}
                                />
                            <Box sx={{ height: 500, width: '100%' }}>
                                <PreviewTable rows={rows} gridRef={gridRef} setRows={setRows} record={record}/>
                            </Box>
                            <Box sx={{ textAlign: 'center' }}>
                                <CloseButton setOpen={setOpen} />
                                <SubmitButton selectedIds={selectedIds} setSelectedIds={setSelectedIds} />
                                <SubmitButton selectedIds={selectedIds} setSelectedIds={setSelectedIds} gridRef={gridRef} record={record}/>
                            </Box>
                        </Card>
                    </Grid>
@@ -185,14 +171,156 @@
    );
}
const PreviewTable = ({ rows, gridRef, setRows, record }) => {
    gridRef.current = useGridApiRef();
//提交按钮
const SubmitButton = ({ selectedIds, setSelectedIds }) => {
    const submit = () => {
        if (selectedIds.length < 1) { return }
        console.log(selectedIds);
    const columns = [
        { field: 'id', headerName: 'ID', width: 40 },
        { field: 'locCode', headerName: '库位', width: 110 },
        { field: 'barcode', headerName: '容器', width: 120 },
        { field: 'batch', headerName: '批次', width: 90 },
        { field: 'unit', headerName: '单位', width: 90 },
        { field: 'outQty', headerName: '本次出库数量', width: 110 },
        {
            field: 'siteNo',
            headerName: '出库口',
            width: 90,
            type: 'singleSelect',
            editable: true,
            renderCell: (params) => (
                <OutStockSiteNo value={params.value} />
            ),
            renderEditCell: (params) => (
                <OutStockSite {...params} />
            ),
        },
        {
            field: 'actions',
            type: 'actions',
            headerName: '操作',
            with: 120,
            getActions: (params) => [
                <GridActionsCellItem
                    icon={<Delete />}
                    label="Delete"
                    onClick={() => handleDelete(params.row, rows, setRows)}
                />,
            ]
        },
    ]
    /**
     * 删除事件
     * @param {*} params
     */
    const handleDelete = (params, rows, setRows) => {
        const outRows = rows.filter(row => {
             return row.id !== params.id
        })
        setRows(outRows)
    }
    const OutStockSiteNo = React.memo(function OutStockSiteNo(props) {
        const { value } = props;
        if (!value) {
            return null;
        }
        return (
            <Box
                sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
            >
                <span>{value}</span>
            </Box>
        );
    });
    const OutStockSite = (params) => {
        const { id, field, siteNo, row: { staNos } } = params;
        const apiRef = useGridApiContext();
        const handleChange = async (event) => {
            await apiRef.current.setEditCellValue(
                { id, field, value: event.target.value },
                event,
            );
            apiRef.current.stopCellEditMode({ id, field });
        };
        const handleClose = (event, reason) => {
            if (reason === 'backdropClick') {
                apiRef.current.stopCellEditMode({ id, field });
            }
        };
        return (
            <Select
                value={siteNo}
                onChange={handleChange}
                MenuProps={{
                    onClose: handleClose,
                }}
                sx={{
                    height: '100%',
                    '& .MuiSelect-select': {
                        display: 'flex',
                        alignItems: 'center',
                        pl: 1,
                    },
                }}
                autoFocus
                fullWidth
                open
            >
                {staNos.map((option) => {
                    return (
                        <MenuItem
                            key={option}
                            value={option.staNo}
                        >
                            <ListItemText sx={{ overflow: 'hidden' }} primary={option.staNo} />
                        </MenuItem>
                    );
                })}
            </Select >
        )
    }
    return (
        <DataGrid
            rows={rows}
            columns={columns}
            apiRef={gridRef}
            initialState={{
                pagination: {
                    paginationModel: {
                        pageSize: 15,
                    },
                },
            }}
            checkboxSelection
            pageSizeOptions={[15, 25, 35, 45]}
            onRowSelectionModelChange={(ids) => {
                setSelectedIds(ids)
            }}
        />
    )
}
//提交按钮
const SubmitButton = ({ selectedIds, setSelectedIds, gridRef, record }) => {
    const notify = useNotify();
    const refresh = useRefresh();
    const redirect = useRedirect();
    const submit = async () => {
        console.log(record);
        const items = gridRef.current?.getSortedRows();
        const { data: { code, data, msg } } = await request.post('/outStock/generate/tasks', { items, outId: record?.id });
        if (code == 200) {
            refresh();
            redirect("/task")
        } else {
            notify(msg);
        }
    }
    return (
        <ConfirmButton
            label="toolbar.confirm"
@@ -219,95 +347,6 @@
            sx={{ margin: '3.5em' }} />
    )
}
const columns = [
    { field: 'id', headerName: 'ID', width: 40 },
    { field: 'locCode', headerName: '库位', width: 110 },
    { field: 'barcode', headerName: '容器', width: 120 },
    { field: 'batch', headerName: '批次', width: 90 },
    { field: 'unit', headerName: '单位', width: 90 },
    { field: 'workQty', headerName: '本次出库数量', width: 110 },
    {
        field: 'siteNo',
        headerName: '出库口',
        width: 90,
        type: 'singleSelect',
        editable: true,
        renderCell: (params) => (
            <OutStockSiteNo value={params.value} />
        ),
        renderEditCell: (params) => (
            <OutStockSite {...params} />
        ),
    },
]
const OutStockSiteNo = React.memo(function OutStockSiteNo(props) {
    const { value } = props;
    if (!value) {
        return null;
    }
    return (
        <Box
            sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
        >
            <span>{value}</span>
        </Box>
    );
});
const OutStockSite = (params) => {
    const { id, field, siteNo, row: { staNos } } = params;
    const apiRef = useGridApiContext();
    const handleChange = async (event) => {
        await apiRef.current.setEditCellValue(
            { id, field, value: event.target.value },
            event,
        );
        apiRef.current.stopCellEditMode({ id, field });
    };
    const handleClose = (event, reason) => {
        if (reason === 'backdropClick') {
            apiRef.current.stopCellEditMode({ id, field });
        }
    };
    return (
        <Select
            value={siteNo}
            onChange={handleChange}
            MenuProps={{
                onClose: handleClose,
            }}
            sx={{
                height: '100%',
                '& .MuiSelect-select': {
                    display: 'flex',
                    alignItems: 'center',
                    pl: 1,
                },
            }}
            autoFocus
            fullWidth
            open
        >
            {staNos.map((option) => {
                return (
                    <MenuItem
                        key={option}
                        value={option.staNo}
                    >
                        <ListItemText sx={{ overflow: 'hidden' }} primary={option.staNo} />
                    </MenuItem>
                );
            })}
        </Select >
    )
}
export default OutStockPublic;
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/McpController.java
@@ -86,7 +86,13 @@
        }
        param.setType(Constants.TASK_TYPE_OUT_STOCK);
        return locItemService.generateTask(param, getLoginUserId());
        try {
            locItemService.generateTask(param, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("任务生成成功");
    }
rsf-server/src/main/java/com/vincent/rsf/server/common/constant/Constants.java
@@ -103,6 +103,11 @@
    public static final String TASK_TYPE_OUT_CHECK = "check";
    /**
     * 单据出库
     */
    public static final String TASK_TYPE_ORDER_OUT_STOCK = "OrderOutStock";
    /**
     * 拣料出库
     */
    public static final String TASK_TYPE_OUT_PICK = "pick";
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
@@ -97,7 +97,13 @@
        }
        param.setType(Constants.TASK_TYPE_OUT_STOCK);
        return locItemService.generateTask(param, getLoginUserId());
        try {
            locItemService.generateTask(param, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok();
    }
    /**
@@ -129,7 +135,13 @@
        }
        map.setType(Constants.TASK_TYPE_OUT_CHECK);
        return R.ok("任务生成成功").add(locItemService.generateTask(map, getLoginUserId()));
        try {
            locItemService.generateTask(map, getLoginUserId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("任务生成成功");
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -8,6 +9,7 @@
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.enums.OrderType;
import com.vincent.rsf.server.manager.enums.OrderWorkType;
import com.vincent.rsf.server.common.annotation.OperationLog;
@@ -28,6 +30,8 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -38,6 +42,8 @@
@RestController
@Api(tags = "出库单据")
public class OutStockController extends BaseController {
    Logger logger = LoggerFactory.getLogger(OutStockController.class);
    @Autowired
    private OutStockService outStockService;
@@ -235,11 +241,35 @@
    @PostMapping("/outStock/order/getOutTaskItems")
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    public R getOrderOutTaskItem(@RequestBody OrderOutTaskParam param){
        if (Cools.isEmpty(param)){
    public R getOrderOutTaskItem(@RequestBody OrderOutTaskParam param) {
        if (Cools.isEmpty(param)) {
            return R.error("参数不能为空");
        }
        return outStockService.getOrderOutTaskItem(param);
    }
    /**
     * 出库单生成出库任务
     * @param params
     * @return
     */
    @PostMapping("/outStock/generate/tasks")
    @ApiOperation("出库单生成出库任务")
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    public R genOutStockTask( @RequestBody Map<String, Object> params) {
        if (Cools.isEmpty()) {
            return R.error("参数不能为空!!");
        }
        if (Objects.isNull(params.get("outId"))) {
            return R.error("出库参数不能为空!!");
        }
        List<OutStockToTaskParams> taskParams = JSONArray.parseArray(JSONArray.toJSONString(params.get("items")), OutStockToTaskParams.class);
        return  outStockService.genOutStockTask(taskParams, getLoginUserId(), Long.parseLong(params.get("outId").toString()));
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/dto/OrderOutItemDto.java
@@ -2,7 +2,6 @@
import com.vincent.rsf.server.manager.entity.LocItem;
import com.vincent.rsf.server.manager.utils.Synchro;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@@ -15,8 +14,7 @@
@Accessors(chain = true)
public class OrderOutItemDto {
    @Delegate(excludes = Synchro.class)
    @Delegate(types = LocItem.class)
    private LocItem locItem;
    private List<staListDto> staNos;
@@ -29,6 +27,7 @@
        private String staName;
    }
    public void sync(Object source) {
        Synchro.Copy(source, this);
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/LocToTaskParams.java
@@ -17,6 +17,9 @@
    @ApiModelProperty("类型: check:盘点出库, outStock: 库存出库")
    private String type;
    @ApiModelProperty("原单据ID (用户单据出库查找业务类型")
    private Long sourceId;
    @ApiModelProperty("目标站点")
    private String siteNo;
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/OutStockToTaskParams.java
New file
@@ -0,0 +1,47 @@
package com.vincent.rsf.server.manager.controller.params;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map;
@Data
@Accessors(chain = true)
@ApiModel(value = "OutStockToTaskParams", description = "出库单生成任务参数")
public class OutStockToTaskParams {
    @ApiModelProperty("库位")
    private String locCode;
    @ApiModelProperty("库位明细ID")
    private Long id;
    @ApiModelProperty("托盘码")
    private String barcode;
    @ApiModelProperty("批次")
    private String batch;
    @ApiModelProperty("单位")
    private String unit;
    @ApiModelProperty("客单号")
    private String platOrderCode;
    @ApiModelProperty("字段索引")
    private String fieldsIndex;
    @ApiModelProperty("扩展字段")
    @TableField(exist = false)
    private Map<String, String> extendFields;
    @ApiModelProperty("数量")
    private Double outQty;
    @ApiModelProperty("站点")
    private String siteNo;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
@@ -172,6 +172,10 @@
    @TableField(exist = false)
    private Map<String, String> extendFields;
    @ApiModelProperty("托盘码")
    @TableField(exist = false)
    private String barcode;
    /**
     * 状态 1: 正常  0: 冻结  
     */
@@ -220,13 +224,13 @@
    private Date updateTime;
    @TableField(exist = false)
    private Long waveId;
    private Long sourceId;
    @TableField(exist = false)
    private Long waveItemId;
    private Long source;
    @TableField(exist = false)
    private String waveCode;
    private String sourceCode;
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java
@@ -26,9 +26,11 @@
    OUT_STOCK_STATUS_TASK_INIT("10", "初始化"),
    OUT_STOCK_STATUS_TASK_EXCE("11", "待处理"),
    OUT_STOCK_STATUS_TASK_WAVE("12", "生成波次"),
    OUT_STOCK_STATUS_TASK_WORKING("13", "作业中")
    ;
    OUT_STOCK_STATUS_TASK_WAVE("11", "生成波次"),
    OUT_STOCK_STATUS_TASK_CREATE("13", "生成工作档"),
    OUT_STOCK_STATUS_TASK_WORKING("14", "作业中")
            ;
    AsnExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java
@@ -8,7 +8,7 @@
public interface LocItemService extends IService<LocItem> {
    R generateTask(LocToTaskParams map, Long loginUserId);
    void generateTask(LocToTaskParams map, Long loginUserId) throws Exception;
    Task genMoveTask(LocToTaskParams map, Long loginUserId);
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -4,6 +4,7 @@
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.entity.AsnOrder;
import java.util.List;
@@ -21,4 +22,6 @@
    R updateOrderItem(AsnOrderAndItemsParams params, Long loginUserId);
    R getOrderOutTaskItem(OrderOutTaskParam param);
    R genOutStockTask(List<OutStockToTaskParams> params, Long loginUserId, Long outId);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -7,7 +7,6 @@
import com.vincent.rsf.server.api.entity.dto.InTaskMsgDto;
import com.vincent.rsf.server.api.service.WcsService;
import com.vincent.rsf.server.api.utils.LocUtils;
import com.vincent.rsf.server.api.utils.SlaveProperties;
import com.vincent.rsf.server.common.constant.Constants;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.entity.*;
@@ -19,6 +18,8 @@
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -29,6 +30,8 @@
@Service("locItemService")
public class LocItemServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements LocItemService {
    Logger logger = LoggerFactory.getLogger(LocItemServiceImpl.class);
    @Autowired
    private LocService locService;
@@ -42,6 +45,8 @@
    private DeviceSiteService deviceSiteService;
    @Autowired
    private WcsService wcsService;
    @Autowired
    private OutStockService outStockService;
    /**
@@ -55,7 +60,7 @@
    @Override
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    public R generateTask(LocToTaskParams map, Long loginUserId) {
    public void generateTask(LocToTaskParams map, Long loginUserId) throws Exception {
        if (Objects.isNull(map.getSiteNo())) {
            throw new CoolException("站点不能为空!");
        }
@@ -66,19 +71,31 @@
        String siteNo = map.getSiteNo();
        List<LocItem> items = map.getItems();
        Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId));
        AsnOrder order;
        if (!Objects.isNull(map.getSourceId())) {
            order = outStockService.getById(map.getSourceId());
        } else {
            order = new AsnOrder();
        }
        listMap.keySet().forEach(key -> {
            Task task = new Task();
            Loc loc = locService.getById(key);
            logger.info("库位:>{}<UNK>", loc.getCode());
            if (Objects.isNull(loc)) {
                throw new CoolException("数据错误:所选库存信息不存在!!");
            }
            if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) {
                throw new CoolException("库位:" + loc.getCode() +  ",不处于F.在库状态,不可执行R.出库预约操作!!");
            }
            loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
            if (!locService.updateById(loc)) {
                throw new CoolException("库位状态更新失败!!");
            }
            Task moveTask = new Task();
            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
            task.setOrgLoc(loc.getCode())
                    .setTaskCode(ruleCode)
@@ -101,12 +118,12 @@
            List<LocItem> locItemList = listMap.get(key);
            Double outQty = locItemList.stream().mapToDouble(LocItem::getOutQty).sum();
            Task serviceOne = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, loc.getBarcode()));
            if (!Objects.isNull(serviceOne)) {
                throw new CoolException("托盘任务执行中,不能重复创建!");
            }
//            Task serviceOne = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, loc.getBarcode()));
//            if (!Objects.isNull(serviceOne)) {
//                throw new CoolException("托盘任务执行中,不能重复创建!");
//            }
            if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) {
            if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK) || map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) {
                Double useQty = Math.round((outQty + workQty) * 10000) / 10000.0;
                if (orgQty.compareTo(useQty) > 0) {
                    //拣料出库
@@ -140,6 +157,7 @@
                }
                task.setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type).setWarehType(deviceSite.getDevice());
            }
            if (!taskService.save(task)) {
                throw new CoolException("任务创建失败!!");
            }
@@ -186,13 +204,19 @@
                        .setBatch(item.getBatch())
                        .setUpdateBy(loginUserId)
                        .setCreateBy(loginUserId)
                        .setSource(item.getId())
                        .setSourceId(item.getLocId())
                        .setSourceCode(item.getLocCode())
                        .setCreateTime(new Date())
                        .setUpdateTime(new Date())
                        .setOrderType(OrderType.ORDER_OUT.type)
                        .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type));
               if (map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) {
                    taskItem.setWkType(Short.parseShort(order.getWkType()))
                            .setSourceCode(order.getCode())
                            .setSourceId(order.getId());
               } else if (map.getType().equals(Constants.TASK_TYPE_OUT_CHECK) || map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) {
                   taskItem.setSource(item.getId())
                           .setSourceId(item.getLocId())
                           .setSourceCode(item.getLocCode());
               }
                taskItems.add(taskItem);
                Double qty = Math.round((item.getWorkQty() + item.getOutQty()) * 10000) / 10000.0;
@@ -222,8 +246,6 @@
                throw new CoolException("任务明细生成失败!!");
            }
        });
        return R.ok("任务生成完成!!");
    }
    /**
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -1,16 +1,18 @@
package com.vincent.rsf.server.manager.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.utils.LocUtils;
import com.vincent.rsf.server.common.constant.Constants;
import com.vincent.rsf.server.manager.controller.dto.ExistDto;
import com.vincent.rsf.server.manager.controller.dto.OrderOutItemDto;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.entity.*;
@@ -20,6 +22,8 @@
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -40,6 +44,9 @@
 */
@Service("outStockServiceImpl")
public class OutStockServiceImpl extends ServiceImpl<AsnOrderMapper, AsnOrder> implements OutStockService {
    public Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private AsnOrderItemService asnOrderItemService;
@@ -406,25 +413,108 @@
            throw new CoolException("策略参数为空");
        }
        if (Cools.isEmpty(param.getOrderId())) {
            throw new CoolException("单据id为空");
            throw new CoolException("单据ID为空");
        }
        WaveRule waveRule = waveRuleService.getOne(new LambdaQueryWrapper<WaveRule>().eq(WaveRule::getId, param.getWaveId()));
        if (Cools.isEmpty(waveRule)) {
            throw new CoolException("未找到当前策略");
        }
        List<OrderOutItemDto> locItems = null;
        if (WaveRuleType.Efficiency_First.type.equals(waveRule.getType())) {
            locItems = efficiencyFirst(param.getOrderId());
        } else if (WaveRuleType.First_In_First_Out.type.equals(waveRule.getType())) {
        }
        locItems = getOutOrderList(param.getOrderId(), waveRule);
        return R.ok(locItems);
    }
    /**
     * 生成出库任务
     *
     * @param params
     * @param outId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R genOutStockTask(List<OutStockToTaskParams> params, Long loginUserId, Long outId) {
        if (params.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        //优先生成浅库位任务
        List<OutStockToTaskParams> Items = params.stream().sorted(Comparator.comparing(OutStockToTaskParams::getLocCode).thenComparing(item -> {
            return LocUtils.isShallowLoc(item.getLocCode()) ? 1 : 0;
        }).reversed()).collect(Collectors.toList());
        for (OutStockToTaskParams param : Items) {
            if (Objects.isNull(param)) {
                continue;
            }
            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, param.getBarcode()));
            if (!Objects.isNull(loc)) {
                List<LocItem> locItems = new ArrayList<>();
                LocItem locItem = locItemService.getById(param.getId());
                AsnOrderItem orderItem = outStockItemService.getOne(new LambdaQueryWrapper<AsnOrderItem>()
                        .eq(AsnOrderItem::getAsnId, outId)
                        .eq(StringUtils.isNotBlank(locItem.getBatch()), AsnOrderItem::getSplrBatch, locItem.getBatch())
                        .eq(StringUtils.isNotBlank(locItem.getFieldsIndex()), AsnOrderItem::getFieldsIndex, locItem.getFieldsIndex())
                        .eq(AsnOrderItem::getMatnrId, locItem.getMatnrId()));
                if (Objects.isNull(orderItem)) {
                    throw new CoolException("单据明细不存在!!");
                }
                locItem.setOutQty(param.getOutQty())
                        .setBatch(param.getBatch())
                        .setSourceId(outId)
                        .setSourceCode(orderItem.getAsnCode())
                        .setSource(orderItem.getId());
                locItems.add(locItem);
                LocToTaskParams taskParams = new LocToTaskParams();
                taskParams.setType(Constants.TASK_TYPE_ORDER_OUT_STOCK)
                        .setOrgLoc(loc.getCode())
                        .setItems(locItems)
                        .setSourceId(outId)
                        .setSiteNo(param.getSiteNo());
                try {
                    //生成出库任务
                    locItemService.generateTask(taskParams, loginUserId);
                } catch (Exception e) {
                    logger.error("UNK", e);
                    throw new CoolException(e.getMessage());
                }
                Double workQty = Math.round((orderItem.getWorkQty() + locItem.getOutQty()) * 10000) / 10000.0;
                orderItem.setUpdateBy(loginUserId).setUpdateTime(new Date()).setWorkQty(workQty);
                if (!outStockItemService.updateById(orderItem)) {
                    throw new CoolException("单据明细修改失败!!");
                }
            }
        }
        Double sum = Items.stream().mapToDouble(OutStockToTaskParams::getOutQty).sum();
        //更新出库单明细及主单
        AsnOrder outOrder = outStockService.getById(outId);
        if (Objects.isNull(outOrder)) {
            throw new CoolException("出库单据不存在!!");
        }
        Double workQty = Math.round((outOrder.getWorkQty() + sum) * 10000) / 10000.0;
        outOrder.setWorkQty(workQty).setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_CREATE.val);
        if (!outStockService.updateById(outOrder)) {
            throw new CoolException("出库单状态修改失败!!");
        }
        return R.ok();
    }
    private List<LocItem> getEfficiencyFirstItemList(AsnOrderItem asnOrderItem) {
        QueryWrapper<LocItem> locItemQueryWrapper = new QueryWrapper<>();
        locItemQueryWrapper.eq("matnr_code", asnOrderItem.getMatnrCode());
        locItemQueryWrapper.eq("batch", asnOrderItem.getSplrBatch());
        LambdaQueryWrapper<LocItem> locItemQueryWrapper = new LambdaQueryWrapper<>();
        locItemQueryWrapper.eq(LocItem::getMatnrCode, asnOrderItem.getMatnrCode());
        locItemQueryWrapper.eq(LocItem::getBatch, asnOrderItem.getSplrBatch());
        String applySql = String.format(
                "EXISTS (SELECT 1 FROM man_loc ml " +
                        "WHERE ml.use_status = '%s'" +
@@ -434,18 +524,33 @@
        );
        locItemQueryWrapper.apply(applySql);
        List<LocItem> locItems = locItemService.list(locItemQueryWrapper);
        locItems.sort((s1, s2) -> LocUtils.isShallowLoc(s1.getLocCode()) ? -1 : 0);
        locItems.sort(Comparator
                .comparing((LocItem item) -> !LocUtils.isShallowLoc(item.getLocCode()))
        );
        return locItems;
    }
    private List<OrderOutItemDto> efficiencyFirst(Long orderId) {
        List<AsnOrderItem> asnOrderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
                .eq(AsnOrderItem::getAsnId, orderId)
    private List<LocItem> getFirstInFirstOutItemList(AsnOrderItem asnOrderItem) {
        LambdaQueryWrapper<LocItem> locItemQueryWrapper = new LambdaQueryWrapper<>();
        locItemQueryWrapper.eq(LocItem::getMatnrCode, asnOrderItem.getMatnrCode());
        locItemQueryWrapper.eq(LocItem::getBatch, asnOrderItem.getSplrBatch());
        locItemQueryWrapper.orderByAsc(LocItem::getCreateTime);
        String applySql = String.format(
                "EXISTS (SELECT 1 FROM man_loc ml " +
                        "WHERE ml.use_status = '%s'" +
                        "AND ml.id = man_loc_item.loc_id " +
                        ")",
                LocStsType.LOC_STS_TYPE_F.type
        );
        locItemQueryWrapper.apply(applySql);
        List<LocItem> locItems = locItemService.list(locItemQueryWrapper);
        return locItems;
    }
    private List<OrderOutItemDto> getOutOrderList(Long orderId, WaveRule waveRule) {
        List<AsnOrderItem> asnOrderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
                .eq(AsnOrderItem::getAsnId, orderId));
        List<OrderOutItemDto> list = new ArrayList<>();
        Set<String> exist = new HashSet<>();
        Set<ExistDto> existDtos = new HashSet<>();
        for (AsnOrderItem asnOrderItem : asnOrderItems) {
@@ -456,32 +561,42 @@
                continue;
            }
            List<LocItem> locItems = null;
            locItems = getEfficiencyFirstItemList(asnOrderItem);
            if (WaveRuleType.Efficiency_First.type.equals(waveRule.getType())) {
                locItems = getEfficiencyFirstItemList(asnOrderItem);
            } else if (WaveRuleType.First_In_First_Out.type.equals(waveRule.getType())) {
                locItems = getFirstInFirstOutItemList(asnOrderItem);
            } else {
                locItems = getFirstInFirstOutItemList(asnOrderItem);
            }
            for (LocItem locItem : locItems) {
                Loc loc = locService.getById(locItem.getLocId());
                List<LocItem> itemList = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocCode, locItem.getLocCode()));
                if (issued.doubleValue() > 0) {
                    ExistDto existDto = new ExistDto().setBatch(locItem.getBatch()).setMatnr(locItem.getMatnrCode()).setLocNo(locItem.getLocCode());
                    if (existDtos.add(existDto)) {
                        locItem.setWorkQty(issued.doubleValue() >= locItem.getAnfme() ? locItem.getAnfme() : issued.doubleValue());
                        locItem.setOutQty(issued.doubleValue() >= locItem.getAnfme() ? locItem.getAnfme() : issued.doubleValue());
                        locItem.setBarcode(loc.getBarcode());
                        OrderOutItemDto orderOutItemDto = new OrderOutItemDto();
                        orderOutItemDto.setLocItem(locItem);
                        List<DeviceSite> deviceSites = deviceSiteService.list(new LambdaQueryWrapper<DeviceSite>()
                                .eq(DeviceSite::getChannel, loc.getChannel())
                                .eq(DeviceSite::getType, issued.doubleValue() >= locItem.getAnfme() && itemList.size() == 1 ? TaskType.TASK_TYPE_OUT.type : TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                        );
                        if (!deviceSites.isEmpty()) {
                            List<OrderOutItemDto.staListDto> maps = new ArrayList<>();
                            for (DeviceSite sta : deviceSites) {
                                OrderOutItemDto.staListDto staListDto = new OrderOutItemDto.staListDto();
                                staListDto.setStaNo(sta.getSite());
                                staListDto.setStaName(sta.getSite());
                                maps.add(staListDto);
                            }
                            orderOutItemDto.setStaNos(maps);
                            //默认获取第一站点
                            DeviceSite deviceSite = deviceSites.stream().findFirst().get();
                            orderOutItemDto.setSiteNo(deviceSite.getSite());
                        }
                        List<OrderOutItemDto.staListDto> maps = new ArrayList<>();
                        for (DeviceSite sta : deviceSites) {
                            OrderOutItemDto.staListDto staListDto = new OrderOutItemDto.staListDto();
                            staListDto.setStaNo(sta.getSite());
                            staListDto.setStaName(sta.getSite());
                            maps.add(staListDto);
                        }
                        orderOutItemDto.setStaNos(maps);
                        list.add(orderOutItemDto);
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -79,6 +79,10 @@
    private LocItemWorkingService locItemWorkingService;
    @Autowired
    private WcsService wcsService;
    @Autowired
    private OutStockService outStockService;
    @Autowired
    private OutStockItemService outStockItemService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -519,6 +523,7 @@
    /**
     * 任务完成后,判断深库位是否为空,如果为空生成移库任务
     *
     * @param loginUserId
     * @param curLoc
     */
@@ -785,17 +790,47 @@
            if (!taskItems.isEmpty()) {
                for (TaskItem item : taskItems) {
                    if (item.getOrderType().equals(OrderType.ORDER_OUT.type)) {
                        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getOrgLoc()));
                        if (Objects.isNull(loc)) {
                            throw new CoolException("数据错误:库位信息不存在!!");
                        }
                        loc.setUseStatus(LocStsType.LOC_STS_TYPE_F.type).setBarcode(task.getBarcode()).setUpdateBy(loginUserId).setUpdateTime(new Date());
                        if (!locService.updateById(loc)) {
                            throw new CoolException("库位信息修改失败!!");
                        }
                        //出库
                        if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type))) {
                            //库存出库
                            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getId, item.getSourceId()));
                            if (Objects.isNull(loc)) {
                                throw new CoolException("数据错误:库位信息不存在!!");
                        } else if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_OTHER.type))) {
                            //其它出库
                        } else {
                            //出库单出库
                            AsnOrder asnOrder = outStockService.getById(item.getSourceId());
                            if (Objects.isNull(asnOrder)) {
                                throw new CoolException("数据错误:单据已不存在!!");
                            }
                            Double workQty = Math.round((asnOrder.getWorkQty() - item.getAnfme()) * 10000) / 10000.0;
                            asnOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                                    .setWorkQty(workQty.compareTo(0.00) > 0 ? workQty : 0.00 )
                                    .setUpdateBy(loginUserId)
                                    .setUpdateTime(new Date());
                            if (!outStockService.updateById(asnOrder)) {
                                throw new CoolException("原单据状态修改失败!!");
                            }
                            loc.setUseStatus(LocStsType.LOC_STS_TYPE_F.type).setBarcode(task.getBarcode()).setUpdateBy(loginUserId).setUpdateTime(new Date());
                            if (!locService.updateById(loc)) {
                                throw new CoolException("库位信息修改失败!!");
                            AsnOrderItem orderItem = outStockItemService.getById(item.getSource());
                            if (Objects.isNull(orderItem)) {
                                throw new CoolException("数据错误:单据明细已不存在!!");
                            }
                            Double workItmQty = Math.round((orderItem.getWorkQty() - item.getAnfme()) * 10000) / 10000.0;
                            orderItem.setWorkQty(workItmQty);
                            if (!outStockItemService.updateById(orderItem)) {
                                throw new CoolException("原单据明细修改失败!!");
                            }
                        }
                    } else {
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -125,9 +125,9 @@
            /***将有货有的明细信息存放到库位信息中*/
            for (int i = 0; i < items.size(); i++) {
                items.get(i)
                        .setWaveId(param.getWaveId())
                        .setWaveCode(param.getWaveCode())
                        .setWaveItemId(param.getId());
                        .setSourceId(param.getWaveId())
                        .setSourceCode(param.getWaveCode())
                        .setSource(param.getId());
            }
            locItemList.addAll(items);
        }
@@ -141,7 +141,7 @@
        listMap.keySet().forEach(key -> {
            List<LocItem> locItems = listMap.get(key);
            LocItem item1 = locItems.stream().findFirst().get();
            WaveItem waveItem = waveItemService.getById(item1.getWaveItemId());
            WaveItem waveItem = waveItemService.getById(item1.getSource());
            if (null == waveItem || Objects.isNull(waveItem)) {
                throw new CoolException("数据错误:波次明细不存在!!");
            }
@@ -183,7 +183,7 @@
                        .setId(null)
                        .setSourceCode(wave.getCode())
                        .setSourceId(wave.getId())
                        .setSource(item.getWaveItemId());
                        .setSource(item.getSource());
                taskItems.add(taskItem);
            }
            if (!taskItemService.saveBatch(taskItems)) {
rsf-server/src/main/resources/application.yml
@@ -25,7 +25,7 @@
  #  global-config:
  #    field-strategy: 0
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
    cache-enabled: true
    call-setters-on-nulls: true