chen.lin
3 天以前 4c66319211f9f7e496dfc32718dbd7aefed4ca88
任务管理完结、取消按钮
14个文件已修改
540 ■■■■■ 已修改文件
rsf-admin/src/page/orders/check/CheckOrderItemList.jsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/check/CheckOrderPub.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/check/MatnrInfoModal.jsx 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/check/SelectMatnrModal.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/task/TaskList.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckLocQueryParams.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Task.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java 175 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/check/CheckOrderItemList.jsx
@@ -2,59 +2,27 @@
import { useNavigate, useLocation } from 'react-router-dom';
import {
  List,
  DatagridConfigurable,
  Datagrid,
  SearchInput,
  TopToolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
  CreateButton,
  ExportButton,
  BulkDeleteButton,
  WrapperField,
  useRecordContext,
  useTranslate,
  useNotify,
  useListContext,
  FunctionField,
  TextField,
  NumberField,
  DateField,
  BooleanField,
  ReferenceField,
  TextInput,
  DateTimeInput,
  DateInput,
  SelectInput,
  NumberInput,
  ReferenceInput,
  ReferenceArrayInput,
  AutocompleteInput,
  DeleteButton,
  Button,
  useEditContext,
  useGetRecordId,
  useGetOne
} from 'react-admin';
import { Box, Typography, Card, Stack, Dialog, DialogActions, DialogTitle } from '@mui/material';
import { styled } from '@mui/material/styles';
import { Box } from '@mui/material';
import PageDrawer from "../../components/PageDrawer";
import BillStatusField from '../../components/BillStatusField';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
  '& .css-1vooibu-MuiSvgIcon-root': {
    height: '.9em',
  },
  '& .RaDatagrid-row': {
    cursor: 'auto'
  },
  '& .column-name': {
  },
  '& .opt': {
    width: 200
  },
}));
import { PAGE_DRAWER_WIDTH, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
const filters = [
  <SearchInput source="condition" alwaysOn />,
@@ -93,20 +61,20 @@
  const [drawerVal, setDrawerVal] = useState(false);
  const [select, setSelect] = useState({});
  const asnId = useGetRecordId();
  const { data: dicts, isPending, error } = useGetOne('outStock', { id: asnId });
  if (asnId == null || asnId === undefined) {
    return <Box sx={{ flexGrow: 1, minHeight: 200 }} />;
  }
  return (
    <>
      <Box display="flex">
      <Box display="flex" sx={{ minWidth: 0 }}>
        <List
          resource="checkItem"
          storeKey='checkItem'
          storeKey={`checkItem-detail-${asnId}`}
          sx={{
            flexGrow: 1,
            transition: (theme) =>
              theme.transitions.create(['all'], {
                duration: theme.transitions.duration.enteringScreen,
              }),
            minWidth: 0,
            marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
          }}
          title={"menu.checkItem"}
@@ -117,18 +85,14 @@
          actions={(
            <TopToolbar>
              <FilterButton />
              <SelectColumnsButton preferenceKey='checkItem' />
            </TopToolbar>
          )}
          perPage={DEFAULT_ITEM_PAGE_SIZE}
        >
          <StyledDatagrid
            preferenceKey='checkItem'
          <Datagrid
            bulkActionButtons={false}
            rowClick={false}
            omit={['id', 'createTime', 'createBy$', 'memo', 'poDetlId', 'purQty', 'trackCode', 'qty','qrcode', 'splrName', 'matnrId', 'orderId']}
          >
            <NumberField source="id" />
            <NumberField source="orderId" label="table.field.checkOrderItem.orderId" />
            <TextField source="orderCode" label="table.field.checkOrderItem.orderCode" />
            <TextField source="matnrId" label="table.field.checkOrderItem.matnrId" />
@@ -137,17 +101,13 @@
            <TextField source="platOrderCode" label="table.field.checkOrderItem.platOrderCode" />
            <NumberField source="anfme" label="table.field.checkOrderItem.anfme" />
            <NumberField source="workQty" label="table.field.checkOrderItem.workQty" />
            <NumberField source="qty" label="table.field.checkOrderItem.qty" />
            <TextField source="stockUnit" label="table.field.checkOrderItem.stockUnit" />
            <TextField source="splrBatch" label="table.field.checkOrderItem.splrBatch" />
            <TextField source="splrCode" label="table.field.checkOrderItem.splrCode" />
            <TextField source="splrName" label="table.field.checkOrderItem.splrName" />
            <DateField source="updateTime" label="common.field.updateTime" showTime />
            <TextField source="updateBy$" label="common.field.updateBy" />
            <TextField source="createBy$" label="common.field.createBy" />
            <DateField source="createTime" label="common.field.createTime" showTime />
            <TextField source="memo" label="common.field.memo" sortable={false} />
          </StyledDatagrid>
          </Datagrid>
        </List>
        {/* <OutOrderItemCreate
rsf-admin/src/page/orders/check/CheckOrderPub.jsx
@@ -70,7 +70,7 @@
    const getLocs = async (ids) => {
        let params = { matnrCode: ids }
        let params = { matnrCode: ids };
        const { data: { code, data, msg } } = await request.post('/check/locs/', params);
        if (code === 200) {
            setRows(data)
rsf-admin/src/page/orders/check/MatnrInfoModal.jsx
@@ -9,22 +9,18 @@
    TextField,
    Box,
    Button,
    Paper,
    styled
} from '@mui/material';
import { EDIT_MODE, DEFAULT_START_PAGE, DEFAULT_PAGE_SIZE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import { DEFAULT_START_PAGE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import DialogCloseButton from "../../components/DialogCloseButton";
import { useTranslate, useNotify, useRefresh } from 'react-admin';
import { useTranslate, useNotify } from 'react-admin';
import request from '@/utils/request';
import { DataGrid } from '@mui/x-data-grid';
import SaveIcon from '@mui/icons-material/Save';
import TreeSelectInput from "@/page/components/TreeSelectInput";
const MatnrInfoModal = (props) => {
    const { open, setOpen, data, setData } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const refresh = useRefresh();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
@@ -41,65 +37,74 @@
    const [isLoading, setIsLoading] = useState(false);
    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(() => ({
            [name]: value
        }));
        setFormData(prev => ({ ...prev, [name]: value }));
    };
    const reset = () => {
        setFormData({
            name: null,
            code: null,
            groupId: null
        })
        setFormData({})
    }
    // 添加物料:从总库存选,多选同物料则数量叠加;下发任务时再选具体库位
    const handleSubmit = () => {
        const hasarr = data.map(el => +el.matnrId)
        const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id)));
        const value = selectedData.map((el => {
            const dynamicFields = dyFields.reduce((acc, item) => {
                acc[item.fields] = el['extendFields']?.[item.fields] || '';
                return acc;
            }, {});
            return {
                matnrId: el.id,
                maktx: el.name,
                matnrCode: el.code,
                stockUnit: el.stockUnit || '',
                purUnit: el.purchaseUnit || '',
                ...dynamicFields
        const hasarr = (data || []).map(el => +el.matnrId);
        const selectedLocItems = selectedRows
            .map(id => tableData.find(row => row.id === id))
            .filter(Boolean)
            .filter(row => !hasarr.includes(+row.matnrId));
        const byMatnr = {};
        selectedLocItems.forEach(el => {
            const k = el.matnrId;
            if (!byMatnr[k]) {
                const dynamicFields = (dyFields || []).reduce((acc, item) => {
                    acc[item.fields] = el.extendFields?.[item.fields] || '';
                    return acc;
                }, {});
                byMatnr[k] = {
                    matnrId: el.matnrId,
                    maktx: el.maktx,
                    matnrCode: el.matnrCode,
                    anfme: 0,
                    stockUnit: el.unit || el.stockUnit || '',
                    purUnit: '',
                    splrBatch: el.splrBatch || '',
                    splrCode: el.splrCode ?? '',
                    splrName: el.splrName ?? '',
                    ...dynamicFields
                };
            }
        }))
        setData([...data, ...value]);
            byMatnr[k].anfme = (byMatnr[k].anfme || 0) + (el.anfme || 0);
        });
        const value = Object.values(byMatnr);
        setData(prev => [...(prev || []), ...value]);
        setOpen(false);
        reset();
    };
    const getData = async () => {
        setIsLoading(true)
        const res = await request.post(`/matnr/page`, {
    const getData = async (pageNo) => {
        setIsLoading(true);
        const currentPage = pageNo !== undefined ? pageNo : page?.page;
        const res = await request.post(`/locItem/useO/page`, {
            ...formData,
            current: page?.page,
            pageSize: page?.pageSize,
            current: currentPage,
            pageSize: page?.pageSize ?? DEFAULT_PAGE_SIZE,
            orderBy: "create_time desc"
        });
        if (res?.data?.code === 200) {
            setTableData(res.data.data.records);
            setRowCount(res.data?.data?.total);
            setTableData(res.data.data.records || []);
            setRowCount(res.data.data?.total ?? 0);
        } else {
            notify(res.data.msg);
        }
        setIsLoading(false)
        setIsLoading(false);
    };
    useEffect(() => {
        getData();
        if (open) getData();
    }, [open, page]);
    const handleSearch = () => {
        getData()
        setPage(p => ({ ...p, page: 1 }));
        getData(1);
    };
    return (
@@ -125,32 +130,31 @@
            <DialogContent sx={{ mt: 2 }}>
                <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                    <Grid container spacing={2}>
                        <Grid item md={4}>
                        <Grid item md={2}>
                            <TextField
                                label={translate('table.field.matnr.name')}
                                name="name"
                                value={formData.name}
                                label={translate('table.field.locItem.locCode')}
                                name="locCode"
                                value={formData.locCode ?? ''}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                        <Grid item md={4}>
                        <Grid item md={2}>
                            <TextField
                                label={translate('table.field.matnr.code')}
                                name="code"
                                value={formData.code}
                                label={translate('table.field.locItem.maktx')}
                                name="maktx"
                                value={formData.maktx ?? ''}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                        <Grid item md={4}>
                            <TreeSelectInput
                                label="table.field.matnr.groupId"
                                value={formData.groupId}
                                resource={'matnrGroup'}
                                source="groupId"
                                name="groupId"
                        <Grid item md={2}>
                            <TextField
                                label={translate('table.field.locItem.matnrCode')}
                                name="matnrCode"
                                value={formData.matnrCode ?? ''}
                                onChange={handleChange}
                                size="small"
                            />
                        </Grid>
                    </Grid>
@@ -163,7 +167,6 @@
                <Box sx={{ mt: 2, height: 400, width: '100%' }}>
                    <AsnWareModalTable
                        tableData={tableData}
                        setTableData={setTableData}
                        dyFields={dyFields}
                        setDyFields={setDyFields}
                        page={page}
@@ -188,24 +191,17 @@
export default MatnrInfoModal;
const AsnWareModalTable = ({ tableData, page, isLoading, pageSize, setPage, rowCount, setTableData, selectedRows, setSelectedRows, dyFields, setDyFields }) => {
const AsnWareModalTable = ({ tableData, page, isLoading, setPage, rowCount, selectedRows, setSelectedRows, dyFields, setDyFields }) => {
    const translate = useTranslate();
    const notify = useNotify();
    const [columns, setColumns] = useState([
        // { field: 'id', headerName: 'ID', width: 100 },
        { field: 'name', headerName: translate('table.field.matnr.name'), width: 300 },
        { field: 'code', headerName: translate('table.field.matnr.code'), width: 200 },
        { field: 'groupId$', headerName: translate('table.field.matnr.groupId'), width: 100 },
        { field: 'spec', headerName: translate('table.field.matnr.spec'), width: 100 },
        { field: 'model', headerName: translate('table.field.matnr.model'), width: 100 },
        { field: 'weight', headerName: translate('table.field.matnr.weight'), width: 100 },
        { field: 'describle', headerName: translate('table.field.matnr.describle'), width: 100 },
        { field: 'nromNum', headerName: translate('table.field.matnr.nromNum'), width: 100 },
        { field: 'unit', headerName: translate('table.field.matnr.unit'), width: 100 },
        { field: 'purchaseUnit', headerName: translate('table.field.matnr.purUnit'), width: 100 },
        { field: 'stockUnit', headerName: translate('table.field.matnr.stockUnit'), width: 100 },
        { field: 'stockLeval$', headerName: translate('table.field.matnr.stockLevel'), width: 100, sortable: false },
        { field: 'locCode', headerName: translate('table.field.locItem.locCode'), width: 100 },
        { field: 'matnrCode', headerName: translate('table.field.locItem.matnrCode'), width: 130 },
        { field: 'maktx', headerName: translate('table.field.locItem.maktx'), width: 250 },
        { field: 'batch', headerName: translate('table.field.locItem.batch'), width: 100 },
        { field: 'anfme', headerName: translate('table.field.locItem.anfme'), width: 100, type: 'number' },
        { field: 'unit', headerName: translate('table.field.locItem.unit'), width: 80 },
    ])
@@ -246,6 +242,7 @@
                size="small"
                rows={tableData}
                columns={columns}
                getRowId={(row) => row.id}
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
rsf-admin/src/page/orders/check/SelectMatnrModal.jsx
@@ -187,7 +187,7 @@
    const [selectedRows, setSelectedRows] = useState([]);
    const handleDeleteItem = () => {
        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
        const newTableData = (tabelData || []).filter((item) => !selectedRows.includes(item.matnrId));
        setTableData(newTableData);
    }
@@ -514,7 +514,7 @@
    const handleDelete = (row) => {
        const newData = _.filter(cdata.current, (item) => item.matnrId !== row.matnrId);
        const newData = (cdata.current || []).filter((item) => item.matnrId !== row.matnrId);
        setTableData(newData);
    };
rsf-admin/src/page/task/TaskList.jsx
@@ -371,14 +371,13 @@
        }
    }
    return (
        (record.taskStatus == 1 || record.taskStatus == 101) && (record.taskType == 1 || record.taskType == 101 || record.taskType == 10 || record.taskType == 107 || record.taskType == 103 || record.taskType == 11) ?
        record?.canCancel === true ? (
            <ConfirmButton
                onConfirm={clickCancel}
                startIcon={<CancelIcon />}
                label={"toolbar.cancel"}>
            </ConfirmButton>
            :
            <></>
        ) : <></>
    )
}
/**
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
@@ -14,8 +14,10 @@
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.server.common.utils.FieldsUtils;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.entity.Companys;
import com.vincent.rsf.server.manager.entity.LocItem;
import com.vincent.rsf.server.manager.enums.TaskResouceType;
import com.vincent.rsf.server.manager.service.CompanysService;
import com.vincent.rsf.server.manager.service.LocItemService;
import com.vincent.rsf.server.manager.service.LocService;
import com.vincent.rsf.server.system.controller.BaseController;
@@ -35,6 +37,8 @@
    private LocItemService locItemService;
    @Autowired
    private LocService locService;
    @Autowired
    private CompanysService companysService;
    @PreAuthorize("hasAuthority('manager:locItem:list')")
    @PostMapping("/locItem/page")
@@ -78,6 +82,12 @@
                Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex());
                record.setExtendFields(fields);
            }
            if (record.getSplrId() != null) {
                Companys companys = companysService.getById(record.getSplrId());
                if (companys != null) {
                    record.setSplrCode(companys.getCode()).setSplrName(companys.getName());
                }
            }
        }
        page.setRecords(records);
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckLocQueryParams.java
@@ -18,6 +18,6 @@
   @ApiModelProperty("巷道")
   private String channel;
   @ApiModelProperty("库们编码")
   @ApiModelProperty("库位编码")
   private String locCode;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
@@ -224,6 +224,14 @@
    @ApiModelProperty("供应商ID")
    private Long splrId;
    @ApiModelProperty("供应商编码(关联查询带出,非表字段)")
    @TableField(exist = false)
    private String splrCode;
    @ApiModelProperty("供应商名称(关联查询带出,非表字段)")
    @TableField(exist = false)
    private String splrName;
    /**
     * 修改时间
     */
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Task.java
@@ -56,6 +56,11 @@
    @ApiModelProperty(value = "是否可点击完成(有当前步骤且非终态)")
    private Boolean canComplete;
    /** 是否可取消:流程步骤能对应上 taskStatus 且未到最后一步(9999) 时为 true */
    @TableField(exist = false, select = false)
    @ApiModelProperty(value = "是否可取消")
    private Boolean canCancel;
    @ApiModelProperty("上级任务ID")
    private Long parentId;
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
@@ -467,7 +467,10 @@
                } else if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type)
                        || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
                    if (!Cools.isEmpty(task.getResource()) && task.getResource().equals(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val)){
                    if ((!Cools.isEmpty(task.getResource()) && task.getResource().equals(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val))
                            // 盘点出库先到 196(非波次)
                            ||task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)
                    ) {
                        task.setTaskStatus(TaskStsType.AWAIT.id);
                    } else {
                        task.setTaskStatus(TaskStsType.WAVE_SEED.id);
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java
@@ -222,7 +222,9 @@
     */
    @Override
    public R genCheckPreview(OrderOutTaskParam param) {
        List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, param.getOrderId()));
        List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                .eq(WkOrderItem::getOrderId, param.getOrderId())
                .eq(WkOrderItem::getDeleted, 0));
        if (orderItems.isEmpty()) {
            throw new CoolException("数据错误:盘点单明细不存在!!");
        }
@@ -269,98 +271,107 @@
        if (checkParams.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        for (CheckOrderParams checkParam : checkParams) {
            if (checkParam.getItems().isEmpty()) {
        List<Map.Entry<CheckOrderParams, CheckOrderItemParams>> flatList = new ArrayList<>();
        for (CheckOrderParams cp : checkParams) {
            if (cp.getItems() == null || cp.getItems().isEmpty()) {
                continue;
            }
            Map<String, List<CheckOrderItemParams>> listMap = checkParam.getItems().stream()
                    .collect(Collectors.groupingBy(CheckOrderItemParams::getBarcode));
            listMap.keySet().forEach(key -> {
                Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, key));
                if (Objects.isNull(loc)) {
                    throw new CoolException("当前库位不存在!!");
                }
                Task task1 = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, key));
                if (!Objects.isNull(task1)) {
                    throw new CoolException("托盘任务已存在!!");
                }
                List<CheckOrderItemParams> itemParams = listMap.get(key);
                CheckOrderItemParams checkItem = itemParams.stream().findFirst().get();
                Task task = new Task();
                String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
                if (StringUtils.isBlank(ruleCode)) {
                    throw new CoolException("任务号不能为空!!");
                }
                task.setOrgLoc(loc.getCode())
                        .setTaskCode(ruleCode)
                        .setTargSite(checkItem.getSiteNo())
                        .setResource(TaskResouceType.TASK_RESOUCE_CHECK_TYPE.val)
                        .setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type)
                        .setTaskStatus(TaskStsType.MISSION_INITIAL.id)
            for (CheckOrderItemParams ip : cp.getItems()) {
                flatList.add(new AbstractMap.SimpleEntry<>(cp, ip));
            }
        }
        Map<String, List<Map.Entry<CheckOrderParams, CheckOrderItemParams>>> byBarcode = flatList.stream()
                .collect(Collectors.groupingBy(e -> e.getValue().getBarcode()));
        for (String key : byBarcode.keySet()) {
            List<Map.Entry<CheckOrderParams, CheckOrderItemParams>> entries = byBarcode.get(key);
            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getDeleted, 0).eq(Loc::getBarcode, key));
            if (Objects.isNull(loc)) {
                throw new CoolException("当前库位不存在!!");
            }
            Task task1 = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getDeleted, 0).eq(Task::getBarcode, key));
            if (!Objects.isNull(task1)) {
                throw new CoolException("托盘任务已存在!!");
            }
            CheckOrderItemParams checkItem = entries.get(0).getValue();
            Task task = new Task();
            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
            if (StringUtils.isBlank(ruleCode)) {
                throw new CoolException("任务号不能为空!!");
            }
            task.setOrgLoc(loc.getCode())
                    .setTaskCode(ruleCode)
                    .setTargSite(checkItem.getSiteNo())
                    .setResource(TaskResouceType.TASK_RESOUCE_CHECK_TYPE.val)
                    .setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type)
                    .setTaskStatus(TaskStsType.MISSION_INITIAL.id)
                    .setCreateTime(new Date())
                    .setUpdateBy(loginUserId)
                    .setUpdateTime(new Date())
                    .setCreateBy(loginUserId)
                    .setBarcode(key);
            if (!taskService.save(task)) {
                throw new CoolException("盘点任务生成失败!!");
            }
            List<TaskItem> items = new ArrayList<>();
            for (Map.Entry<CheckOrderParams, CheckOrderItemParams> e : entries) {
                CheckOrderParams cp = e.getKey();
                CheckOrderItemParams item = e.getValue();
                TaskItem taskItem = new TaskItem();
                taskItem.setTaskId(task.getId())
                        .setOrderType(OrderType.ORDER_CHECK.type)
                        .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_CHECK.type))
                        .setAnfme(item.getAnfme())
                        .setWorkQty(0.0)
                        .setBatch(cp.getBatch())
                        .setMaktx(cp.getMaktx())
                        .setMatnrCode(cp.getMatnrCode())
                        .setPlatOrderCode(cp.getPlatOrderCode())
                        .setPlatWorkCode(cp.getPlatWorkCode())
                        .setUnit(cp.getStockUnit())
                        .setSpec(cp.getSpec())
                        .setModel(cp.getModel())
                        .setFieldsIndex(cp.getFieldsIndex())
                        .setMatnrId(cp.getMatnrId())
                        .setCreateBy(loginUserId)
                        .setSource(cp.getId())
                        .setSourceId(cp.getOrderId())
                        .setSourceCode(cp.getOrderCode())
                        .setCreateTime(new Date())
                        .setUpdateBy(loginUserId)
                        .setUpdateTime(new Date())
                        .setCreateBy(loginUserId)
                        .setBarcode(key);
                        .setOrderItemId(cp.getId())
                        .setOrderId(cp.getOrderId())
                        .setPlatItemId(cp.getPlatItemId());
                items.add(taskItem);
                if (!taskService.save(task)) {
                    throw new CoolException("盘点任务生成失败!!");
                if (!checkOrderItemService.update(new LambdaUpdateWrapper<WkOrderItem>()
                        .eq(WkOrderItem::getId, cp.getId())
                        .setSql("work_qty = work_qty + " + item.getAnfme())
                        .set(WkOrderItem::getWorkQty, item.getAnfme()))) {
                    throw new CoolException("盘点明细修改失败!!");
                }
                List<TaskItem> items = new ArrayList<>();
                itemParams.forEach(item -> {
                    TaskItem taskItem = new TaskItem();
                    taskItem.setTaskId(task.getId())
                            .setOrderType(OrderType.ORDER_CHECK.type)
                            .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_CHECK.type))
                            .setAnfme(item.getAnfme())
                            .setWorkQty(0.0)
                            .setBatch(checkParam.getBatch())
                            .setMaktx(checkParam.getMaktx())
                            .setMatnrCode(checkParam.getMatnrCode())
                            .setPlatOrderCode(checkParam.getPlatOrderCode())
                            .setPlatWorkCode(checkParam.getPlatWorkCode())
                            .setUnit(checkParam.getStockUnit())
                            .setSpec(checkParam.getSpec())
                            .setModel(checkParam.getModel())
                            .setFieldsIndex(checkParam.getFieldsIndex())
                            .setMatnrId(checkParam.getMatnrId())
                            .setCreateBy(loginUserId)
                            .setSource(checkParam.getId())
                            .setSourceId(checkParam.getOrderId())
                            .setSourceCode(checkParam.getOrderCode())
                            .setCreateTime(new Date())
                            .setUpdateBy(loginUserId)
                            .setUpdateTime(new Date())
                            .setOrderItemId(checkParam.getId())
                            .setOrderId(checkParam.getOrderId())
                            .setPlatItemId(checkParam.getPlatItemId());
                    items.add(taskItem);
            }
            if (!taskItemService.saveBatch(items)) {
                throw new CoolException("任务明细保存失败!!");
            }
                    if (!checkOrderItemService.update(new LambdaUpdateWrapper<WkOrderItem>()
                            .eq(WkOrderItem::getId, checkParam.getId())
                            .setSql("work_qty = work_qty + " + item.getAnfme())
                            .set(WkOrderItem::getWorkQty, checkItem.getAnfme()))) {
                        throw new CoolException("盘点明细修改失败!!");
                    }
                });
                if (!taskItemService.saveBatch(items)) {
                    throw new CoolException("任务明细保存失败!!");
                }
            loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
            if (!locService.updateById(loc)) {
                throw new CoolException("库位预约出库失败!!");
            }
        }
                loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
                if (!locService.updateById(loc)) {
                    throw new CoolException("库位预约出库失败!!");
                }
            });
            Set<Long> checkItemIds = checkParams.stream().map(CheckOrderParams::getOrderId).collect(Collectors.toSet());
            checkItemIds.forEach(orderId -> {
        Set<Long> checkItemIds = checkParams.stream().map(CheckOrderParams::getOrderId).collect(Collectors.toSet());
        checkItemIds.forEach(orderId -> {
                WkOrder order = this.getById(orderId);
                if (Objects.isNull(order)) {
                    throw new CoolException("数据错误:单据不存在!!");
                }
                List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, orderId));
                List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                        .eq(WkOrderItem::getOrderId, orderId)
                        .eq(WkOrderItem::getDeleted, 0));
                if (orderItems.isEmpty()) {
                    throw new CoolException("数据错误:单据明细不存在!!");
                }
@@ -376,7 +387,6 @@
                    throw new CoolException("盘点单信息修改失败!!");
                }
            });
        }
        return R.ok();
    }
@@ -427,7 +437,8 @@
            }
        });
        List<WkOrderItem> orderItems = checkOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                .eq(WkOrderItem::getOrderId, params.getOrders().getId()));
                .eq(WkOrderItem::getOrderId, params.getOrders().getId())
                .eq(WkOrderItem::getDeleted, 0));
        Double sum = orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum();
        orders.setAnfme(sum);
        if (!this.updateById(orders)) {
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -554,6 +554,6 @@
                .eq(StringUtils.isNotBlank(matnr.getLocCode()), LocItem::getLocCode, matnr.getLocCode())
//                .eq(StringUtils.isNotBlank(matnr.getChannel()), LocItem::getChannel, matnr.getChannel())
                .in(!matnr.getMatnrCode().isEmpty(), LocItem::getMatnrCode, matnr.getMatnrCode());
        return  this.baseMapper.listByMatnr(LocStsType.LOC_STS_TYPE_F.type, matnr.getChannel(), wrapper);
        return this.baseMapper.listByMatnr(LocStsType.LOC_STS_TYPE_F.type, matnr.getChannel(), wrapper);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -885,7 +885,13 @@
        Set<Integer> terminalSet = TERMINAL_OR_WAITING_STATUS;
        List<String> codes = tasks.stream().map(Task::getTaskCode).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (codes.isEmpty()) {
            tasks.forEach(t -> t.setCanComplete(Boolean.FALSE));
            tasks.forEach(t -> {
                t.setCanComplete(Boolean.FALSE);
                t.setCanCancel(Boolean.FALSE);
                if (t.getTaskStatus() != null && t.getTaskStatus().equals(TaskStsType.MISSION_INITIAL.id) && !isPhase2InboundType(t.getTaskType())) {
                    t.setCanCancel(Boolean.TRUE);
                }
            });
            return;
        }
        List<FlowStepInstance> steps = codes.size() == 1
@@ -895,11 +901,19 @@
        for (Task task : tasks) {
            if (task.getTaskStatus() != null && terminalSet.contains(task.getTaskStatus())) {
                task.setCanComplete(Boolean.FALSE);
                task.setCanCancel(Boolean.FALSE);
                continue;
            }
            // taskStatus=0(路径规划中)可取消;二阶段入库(拣料/盘点/并板入库)不可取消
            if (task.getTaskStatus() != null && task.getTaskStatus().equals(TaskStsType.MISSION_INITIAL.id)) {
                task.setCanCancel(!isPhase2InboundType(task.getTaskType()));
                task.setCanComplete(Boolean.FALSE);
                continue;
            }
            List<FlowStepInstance> taskSteps = stepsByTaskNo.get(task.getTaskCode());
            if (taskSteps == null || taskSteps.isEmpty()) {
                task.setCanComplete(Boolean.FALSE);
                task.setCanCancel(Boolean.FALSE);
                continue;
            }
            FlowStepInstance match = taskSteps.stream()
@@ -907,11 +921,21 @@
                    .findFirst().orElse(null);
            if (match == null) {
                task.setCanComplete(Boolean.FALSE);
                task.setCanCancel(Boolean.FALSE);
                continue;
            }
            boolean isLastStep = match.getWmsNextTaskStatus() == null || match.getWmsNextTaskStatus().equals(TaskStsType.MISSION_TRANSFER.id);
            task.setCanComplete(!isLastStep);
            task.setCanCancel(!isLastStep && !isPhase2InboundType(task.getTaskType()));
        }
    }
    /** 二阶段入库:拣料入库、盘点入库、并板入库,不可取消 */
    private boolean isPhase2InboundType(Integer taskType) {
        if (taskType == null) return false;
        return taskType.equals(TaskType.TASK_TYPE_PICK_IN.type)
                || taskType.equals(TaskType.TASK_TYPE_CHECK_IN.type)
                || taskType.equals(TaskType.TASK_TYPE_MERGE_IN.type);
    }
    /**
@@ -1227,28 +1251,43 @@
    }
    /**
     * 盘点再入库完成后,将关联的盘点差异单置为已审核(有单按 orderId,无单按 出库任务号 orderCode)
     * 盘点再入库完成后,将本单对应的差异明细置为已审核,全部明细已审核后差异单本身置为已审核(有单按 orderId,无单按 出库任务号 orderCode)
     */
    @Override
    public void markCheckDiffApprovedWhenCheckInDone(Task checkInTask) {
        List<TaskItem> items = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, checkInTask.getId()).last("limit 1"));
        Long orderId = items.isEmpty() ? null : items.get(0).getOrderId();
        Task outTask = taskService.getOne(new LambdaQueryWrapper<Task>()
                .eq(Task::getBarcode, checkInTask.getBarcode())
                .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_OUT.type)
                .last("limit 1"));
        if (outTask == null) {
            return;
        }
        List<TaskItem> inItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, checkInTask.getId()));
        Long orderId = inItems.isEmpty() ? null : inItems.get(0).getOrderId();
        CheckDiff checkDiff = null;
        if (orderId != null && !orderId.equals(0L)) {
            checkDiff = checkDiffService.getOne(new LambdaQueryWrapper<CheckDiff>().eq(CheckDiff::getOrderId, orderId).last("limit 1"));
        } else {
            Task outTask = taskService.getOne(new LambdaQueryWrapper<Task>()
                    .eq(Task::getBarcode, checkInTask.getBarcode())
                    .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_OUT.type)
            checkDiff = checkDiffService.getOne(new LambdaQueryWrapper<CheckDiff>()
                    .eq(CheckDiff::getOrderCode, outTask.getTaskCode())
                    .and(w -> w.isNull(CheckDiff::getOrderId).or().eq(CheckDiff::getOrderId, 0))
                    .last("limit 1"));
            if (outTask != null) {
                checkDiff = checkDiffService.getOne(new LambdaQueryWrapper<CheckDiff>()
                        .eq(CheckDiff::getOrderCode, outTask.getTaskCode())
                        .and(w -> w.isNull(CheckDiff::getOrderId).or().eq(CheckDiff::getOrderId, 0))
                        .last("limit 1"));
            }
        }
        if (checkDiff != null && !CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_END.val.equals(checkDiff.getExceStatus())) {
        if (checkDiff == null) {
            return;
        }
        List<TaskItem> outItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, outTask.getId()));
        List<Long> outTaskItemIds = outItems.stream().map(TaskItem::getId).filter(Objects::nonNull).collect(Collectors.toList());
        if (!outTaskItemIds.isEmpty()) {
            checkDiffItemService.update(new LambdaUpdateWrapper<CheckDiffItem>()
                    .eq(CheckDiffItem::getCheckId, checkDiff.getId())
                    .in(CheckDiffItem::getTaskItemId, outTaskItemIds)
                    .set(CheckDiffItem::getExceStatus, CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_END.val));
        }
        long unApprovedCount = checkDiffItemService.count(new LambdaQueryWrapper<CheckDiffItem>()
                .eq(CheckDiffItem::getCheckId, checkDiff.getId())
                .ne(CheckDiffItem::getExceStatus, CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_END.val));
        if (unApprovedCount == 0 && !CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_END.val.equals(checkDiff.getExceStatus())) {
            checkDiffService.update(new LambdaUpdateWrapper<CheckDiff>()
                    .eq(CheckDiff::getId, checkDiff.getId())
                    .set(CheckDiff::getExceStatus, CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_END.val));
@@ -1265,16 +1304,20 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R removeTask(Long[] ids, Long loginUserId) {
        List<Integer> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id);
        List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type,
                TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_EMPTY_IN.type, TaskType.TASK_TYPE_LOC_MOVE.type,
                TaskType.TASK_TYPE_EMPTY_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type);
                TaskType.TASK_TYPE_EMPTY_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type,
                TaskType.TASK_TYPE_PICK_IN.type, TaskType.TASK_TYPE_CHECK_IN.type);
        List<Task> tasks = this.list(new LambdaQueryWrapper<Task>()
                .in(Task::getTaskType, list)
                .in(Task::getId, ids)
                .in(Task::getTaskStatus, longs));
                .in(Task::getId, ids));
        if (tasks.isEmpty()) {
            throw new CoolException("任务已处执行状态不可取消!!");
            throw new CoolException("任务不存在或类型不允许取消!!");
        }
        fillCanComplete(tasks);
        List<Task> notCancelable = tasks.stream().filter(t -> !Boolean.TRUE.equals(t.getCanCancel())).collect(Collectors.toList());
        if (!notCancelable.isEmpty()) {
            throw new CoolException("部分任务不可取消(拣料/盘点/并板入库为二阶段任务不可取消;其他仅 taskStatus=0 或流程未到 9999 时可取消)!!");
        }
        for (Task task : tasks) {
            //取消移库任务
@@ -1331,6 +1374,17 @@
                            }
                        }
                    });
                    List<Long> orderIds = taskItems.stream()
                            .map(TaskItem::getOrderId)
                            .filter(Objects::nonNull)
                            .filter(id -> !id.equals(0L))
                            .distinct()
                            .collect(Collectors.toList());
                    for (Long orderId : orderIds) {
                        checkOrderService.update(new LambdaUpdateWrapper<WkOrder>()
                                .eq(WkOrder::getId, orderId)
                                .set(WkOrder::getExceStatus, CheckExceStatus.CHECK_ORDER_STATUS_UN_EXCE.val));
                    }
                }
                if (!locService.update(new LambdaUpdateWrapper<Loc>()
@@ -1340,6 +1394,30 @@
                }
            }
            // 出库类任务取消时,回退路径规划阶段占用的目标站点(S→O)
            if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_OUT.type)) {
                if (!Cools.isEmpty(task.getTargSite())) {
                    LambdaQueryWrapper<BasStation> stationWrapper = new LambdaQueryWrapper<BasStation>()
                            .eq(BasStation::getStationName, task.getTargSite())
                            .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_S.type);
                    if (!Cools.isEmpty(task.getBarcode())) {
                        stationWrapper.eq(BasStation::getBarcode, task.getBarcode());
                    }
                    BasStation targStation = basStationService.getOne(stationWrapper);
                    if (targStation != null) {
                        targStation.setUseStatus(LocStsType.LOC_STS_TYPE_O.type);
                        targStation.setBarcode(null);
                        if (!basStationService.updateById(targStation)) {
                            throw new CoolException("释放目标站点失败!!");
                        }
                    }
                }
            }
            if (!Objects.isNull(task.getWarehType()) && task.getWarehType().equals(WarehType.WAREHOUSE_TYPE_AGV.val)) {
                BasStation basStation = null;
                if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type) || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_IN.type)) {
rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml
@@ -22,7 +22,8 @@
                FROM
                    man_loc_item li
                        INNER JOIN man_loc l ON l.id = li.loc_id
                    WHERE l.use_status = #{type}
                    WHERE li.deleted = 0
                      AND l.use_status = #{type}
                    <if test="channel != null">
                        AND l.channel = #{channel}
                    </if>