From 456f4f168f615b1d25fcc88f35efe1d7bf933302 Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期四, 22 五月 2025 10:53:29 +0800
Subject: [PATCH] 库存出库生成任务 优化

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java |  116 +++++++++++++++++++
 rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx                                     |    8 
 rsf-admin/src/page/outWork/outBound/OutBoundList.jsx                                         |  199 +++++++++++++++++++--------------
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java    |    9 +
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java                  |    4 
 rsf-admin/src/i18n/zh.js                                                                     |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java          |    4 
 rsf-admin/src/i18n/en.js                                                                     |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java            |    4 
 9 files changed, 255 insertions(+), 93 deletions(-)

diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index b429a7a..84b91fa 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -1155,6 +1155,8 @@
         error: {
             stock: "Insufficient inventory to deliver 锛侊紒",
             select_error_order: "Please Select Asn Orders",
+            out_stock_qty: "The outbound quantity cannot be greater than the inventory quantity",
+
         }
 
     }
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index c00e157..bde1a48 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -1152,6 +1152,8 @@
         error: {
             stock: "搴撳瓨涓嶈冻锛屾棤娉曟彁浜わ紒锛�", 
             select_error_order: "璇烽�夋嫨閫氱煡鍗�",
+            out_stock_qty: "鍑哄簱鏁伴噺涓嶈兘澶т簬搴撳瓨鏁伴噺",
+
         }
 
     }
diff --git a/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx b/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx
index 45573d3..d3ea701 100644
--- a/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx
+++ b/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx
@@ -20,7 +20,8 @@
     useCreateController,
     useListContext,
     useRefresh,
-    Edit,    
+    Edit,
+    useRedirect,
 } from 'react-admin';
 import {
     Dialog,
@@ -59,13 +60,15 @@
 import { Delete } from '@mui/icons-material';
 import _, { set } from 'lodash';
 import StaSelect from "./StaSelect";
+import { redirect } from "react-router";
+import { number } from "prop-types";
 
-const OutBoundList = () => {    
+const OutBoundList = () => {
 
     const [createDialog, setCreateDialog] = useState(false);
     const [tabelData, setTableData] = useState([]);
     const [selectedRows, setSelectedRows] = useState([]);
-    const [sta,setSta] = useState("");
+    const [sta, setSta] = useState("");
     const notify = useNotify();
     const tableRef = useRef();
     tableRef.current = useGridApiRef();
@@ -75,7 +78,7 @@
         const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
         setTableData(newTableData);
     }
-    
+
     // 娣诲姞涓�涓鐞嗘柊鏁版嵁鐨勫嚱鏁帮紝璁剧疆outQty榛樿鍊�
     const handleSetData = (newData) => {
         // 涓烘柊娣诲姞鐨勬暟鎹缃畂utQty榛樿鍊间负anfme鐨勫��
@@ -86,109 +89,126 @@
         setTableData([...tabelData, ...dataWithDefaultQty]);
     };
 
-    
-    
+
+
     return (
         <>
-        <Card sx={{ p: 2, mb: 2, mt:2}}>
-            <Grid container spacing={2}>                
-                <Grid item xs={12}>
-                    <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
-                        <Typography variant="h6">
-                            {translate('table.field.outBound.stockWithdrawal')}
-                        </Typography>
-                        <Stack direction='row' spacing={2}>
-                            <Button 
-                                variant="contained" 
-                                color="primary"
-                                startIcon={<AddIcon />}
-                                onClick={() => setCreateDialog(true)}
-                            >
-                                {translate('table.field.outBound.withdrawal')}
-                            </Button> 
-                        </Stack>
-                    </Box>
+            <Card sx={{ p: 2, mb: 2, mt: 2 }}>
+                <Grid container spacing={2}>
+                    <Grid item xs={12}>
+                        <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
+                            <Typography variant="h6">
+                                {translate('table.field.outBound.stockWithdrawal')}
+                            </Typography>
+                            <Stack direction='row' spacing={2}>
+                                <Button
+                                    variant="contained"
+                                    color="primary"
+                                    startIcon={<AddIcon />}
+                                    onClick={() => setCreateDialog(true)}
+                                >
+                                    {translate('table.field.outBound.withdrawal')}
+                                </Button>
+                            </Stack>
+                        </Box>
+                    </Grid>
                 </Grid>
-            </Grid>
-        </Card>
-        <Card sx={{ p: 2, mb: 2}}>
-            <Form>
-            <Grid container spacing={2}>                
-                <Grid item xs={12}>
-                    <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
-                        <Typography  variant="h6" >
-                            {translate('table.field.outBound.outSta')}
-                         </Typography> 
-                        <Stack direction='row' spacing={2} minWidth={200}>
-                            <StaSelect
-                                source="sta"
-                                label={translate("table.field.outBound.outSta")} 
-                                onChange={(e) => {
-                                    setSta(e.target.value);
-                                    console.log("绔欑偣宸查�夋嫨:", e.target.value);
-                                }}
-                                size="small"                                
-                                type="1"
-                            />
-                        </Stack>
-                        <Stack direction='row' spacing={2} minWidth={200}>
-                            <SubmitButton 
-                                sta={sta}
-                                data={tabelData}
-                            />
-                        </Stack>
-                    </Box>
-                </Grid>
-            </Grid>
-            </Form>            
-        </Card>
-        <Card sx={{ mb: 2}}>
-            <Box sx={{  }}>
-                <ModalTable tabelData={tabelData} setTableData={setTableData}  selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></ModalTable>
-            </Box>
-        </Card>
-        <LocItemInfoModal
+            </Card>
+            <Card sx={{ p: 2, mb: 2 }}>
+                <Form>
+                    <Grid container spacing={2}>
+                        <Grid item xs={12}>
+                            <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
+                                <Typography variant="h6" >
+                                    {translate('table.field.outBound.outSta')}
+                                </Typography>
+                                <Stack direction='row' spacing={2} minWidth={200}>
+                                    <StaSelect
+                                        source="sta"
+                                        label={translate("table.field.outBound.outSta")}
+                                        onChange={(e) => {
+                                            setSta(e.target.value);
+                                            console.log("绔欑偣宸查�夋嫨:", e.target.value);
+                                        }}
+                                        size="small"
+                                        type="1"
+                                    />
+                                </Stack>
+                                <Stack direction='row' spacing={2} minWidth={200}>
+                                    <SubmitButton
+                                        sta={sta}
+                                        data={tabelData}
+                                        setTableData={setTableData}
+                                    />
+                                </Stack>
+                            </Box>
+                        </Grid>
+                    </Grid>
+                </Form>
+            </Card>
+            <Card sx={{ mb: 2 }}>
+                <Box sx={{}}>
+                    <ModalTable tabelData={tabelData} setTableData={setTableData} selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></ModalTable>
+                </Box>
+            </Card>
+            <LocItemInfoModal
                 open={createDialog}
                 setOpen={setCreateDialog}
                 data={tabelData}
                 setData={handleSetData}
             />
-            
+
         </>
     )
 }
 
 export default OutBoundList;
 
-const SubmitButton = (props) =>{
+const SubmitButton = (props) => {
     const translate = useTranslate();
     const notify = useNotify();
-    const { sta, data } = props;
-    const check = ()=>{
-        if(sta === "" || sta === undefined || sta === null){
+    const redirect = useRedirect();
+    const refresh = useRefresh();
+    const { sta, data, setTableData } = props;
+    const check = () => {
+        if (sta === "" || sta === undefined || sta === null) {
             notify("璇烽�夋嫨绔欑偣");
             return;
         }
-        if(data.length === 0){
+        if (data.length === 0) {
             notify("璇烽�夋嫨鐗╂枡");
             return;
         }
-        http(sta,data);
+        http(sta, data);
+    }
+    const http = async (sta, items) => {
+        console.log(items);
 
-    }    
-    const http = async (sta,data) => {
-        console.log("鎻愪氦鏁版嵁",sta,data);
+        const filter = items.filter(item => (item.outQty + item.workQty) > item.anfme);
+        if (filter.length > 0) {
+            notify(translate('toolbar.request.error.out_stock_qty'))
+            return
+        }
+        const { data: { code, data, msg } } = await request.post(`/locItem/generate/task`, { siteNo: sta, items: items });
+        if (code === 200) {
+            notify(msg);
+            refresh()
+            setTableData([])
+            redirect("/task")
+        } else {
+            notify(msg);
+        }
     }
     return (
-        <Button 
-            variant="contained" 
-            color="primary"            
-            onClick={check}
+        <ConfirmButton
+            variant="contained"
+            color="primary"
+            onConfirm={check}
+            label={"table.field.outBound.createTask"}
         >
-          {translate('table.field.outBound.createTask')}
-        </Button> 
+        </ConfirmButton>
     )
-    
+
 }
 
 const ModalTable = ({ tabelData, setTableData, selectedRows, setSelectedRows, tableRef }) => {
@@ -198,23 +218,32 @@
     const [columns, setColumns] = useState([
         {
             field: 'outQty',
-            headerName: translate('table.field.outBound.outQty')+"*",
+            headerName: translate('table.field.outBound.outQty') + "*",
             width: 100,
-            editable: true,   
-            headerClassName: "custom",         
+            type: 'number',
+            editable: true,
+            headerClassName: "custom",
         },
         {
             field: 'anfme',
             headerName: translate('table.field.locItem.anfme'),
+            type: 'number',
             width: 100,
             editable: false,
-        }, 
+        },
+        {
+            field: 'workQty',
+            headerName: translate('table.field.locItem.workQty'),
+            width: 100,
+            type: 'number',
+            editable: false,
+        },
         {
             field: 'matnrCode',
             headerName: translate('table.field.locItem.matnrCode'),
             width: 130,
             editable: false,
-        }, 
+        },
         {
             field: 'maktx',
             headerName: translate('table.field.locItem.maktx'),
diff --git a/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx b/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx
index 69916c3..3f7d883 100644
--- a/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx
+++ b/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx
@@ -44,7 +44,7 @@
     };
 
     const reset = () => {
-        setFormData({ 
+        setFormData({
         })
     }
 
@@ -129,7 +129,7 @@
                                 onChange={handleChange}
                                 size="small"
                             />
-                        </Grid>                        
+                        </Grid>
                     </Grid>
                 </Box>
                 <Box sx={{ mt: 2 }}>
@@ -172,9 +172,9 @@
         { field: 'maktx', headerName: translate('table.field.locItem.maktx'), width: 300 },
         { field: 'batch', headerName: translate('table.field.locItem.batch'), width: 100 },
         { field: 'anfme', headerName: translate('table.field.locItem.anfme'), width: 100 },
-        
+        { field: 'workQty', headerName: translate('table.field.locItem.workQty'), width: 100 },
         { field: 'unit', headerName: translate('table.field.locItem.unit'), width: 100 },
-        
+
     ])
 
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java
index 703afe3..f6b67dc 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java
@@ -81,8 +81,4 @@
 
         return R.ok();
     }
-
-
-
-
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
index 65b395f..e06bb8b 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocItemController.java
@@ -78,6 +78,15 @@
     }
 
     @PreAuthorize("hasAuthority('manager:locItem:list')")
+    @PostMapping("/locItem/generate/task")
+    public R generateTask(@RequestBody Map<String, Object> map) {
+        if (Objects.isNull(map)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return locItemService.generateTask(map);
+    }
+
+    @PreAuthorize("hasAuthority('manager:locItem:list')")
     @PostMapping("/locItem/list")
     public R list(@RequestBody Map<String, Object> map) {
         return R.ok().add(locItemService.list());
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
index 754ed9c..3247606 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/LocItem.java
@@ -94,6 +94,10 @@
     @ApiModelProperty("椤圭洰鍙�")
     private String projectCode;
 
+    @ApiModelProperty("鍑哄簱鏁伴噺")
+    @TableField(exist = false)
+    private Double outQty;
+
     /**
      * 鐗╂枡鍚嶇О
      */
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java
index ce85d82..108fd02 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/LocItemService.java
@@ -1,8 +1,12 @@
 package com.vincent.rsf.server.manager.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.server.manager.entity.LocItem;
+
+import java.util.Map;
 
 public interface LocItemService extends IService<LocItem> {
 
+    R generateTask(Map<String, Object> map);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
index 9cc069f..6bb75cf 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -1,12 +1,128 @@
 package com.vincent.rsf.server.manager.service.impl;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.server.manager.entity.Loc;
+import com.vincent.rsf.server.manager.entity.Task;
+import com.vincent.rsf.server.manager.entity.TaskItem;
+import com.vincent.rsf.server.manager.enums.*;
 import com.vincent.rsf.server.manager.mapper.LocItemMapper;
 import com.vincent.rsf.server.manager.entity.LocItem;
 import com.vincent.rsf.server.manager.service.LocItemService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.server.manager.service.LocService;
+import com.vincent.rsf.server.manager.service.TaskItemService;
+import com.vincent.rsf.server.manager.service.TaskService;
+import com.vincent.rsf.server.system.constant.SerialRuleCode;
+import com.vincent.rsf.server.system.utils.SerialRuleUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 @Service("locItemService")
 public class LocItemServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements LocItemService {
 
+    @Autowired
+    private LocService locService;
+    @Autowired
+    private TaskService taskService;
+    @Autowired
+    private TaskItemService taskItemService;
+    @Autowired
+    private LocItemService locItemService;
+
+
+    /**
+     * 搴撳瓨鍑哄簱鐢熸垚鍑哄簱浠诲姟
+     *
+     * @param map
+     * @return
+     */
+    @Override
+    public R generateTask(Map<String, Object> map) {
+        if (Objects.isNull(map.get("siteNo"))) {
+            throw new CoolException("绔欑偣涓嶈兘涓虹┖锛�");
+        }
+        if (Objects.isNull(map.get("items"))) {
+            throw new CoolException("鏄庣粏涓嶈兘涓虹┖锛�");
+        }
+        String siteNo = map.get("siteNo").toString();
+        List<LocItem> items = JSONArray.parseArray(JSONArray.toJSONString(map.get("items")), LocItem.class);
+        Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId));
+        listMap.keySet().forEach(key -> {
+            Task task = new Task();
+            Loc loc = locService.getById(key);
+            if (Objects.isNull(loc)) {
+                throw new CoolException("鏁版嵁閿欒锛氭墍閫夊簱瀛樹俊鎭笉瀛樺湪锛侊紒");
+            }
+            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
+            task.setOrgLoc(loc.getCode())
+                    .setTaskCode(ruleCode)
+                    .setTargSite(siteNo)
+                    .setTaskStatus(TaskStsType.GENERATE_OUT.id)
+                    .setBarcode(loc.getBarcode());
+
+            List<LocItem> locItems = this.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, key));
+            if (locItems.isEmpty()) {
+                throw new CoolException("鏁版嵁閿欒锛氭墍閫夊簱瀛樻槑缁嗕笉瀛樺湪锛侊紒");
+            }
+
+            Double orgQty = locItems.stream().mapToDouble(LocItem::getAnfme).sum();
+            List<LocItem> locItemList = listMap.get(key);
+            Double outQty = locItemList.stream().mapToDouble(LocItem::getOutQty).sum();
+
+            if (orgQty.compareTo(outQty) > 0) {
+                //鎷f枡鍑哄簱
+                task.setTaskType(TaskType.TASK_TYPE_PICK_AGAIN_IN.type);
+            } else {
+                //鍏ㄦ澘鍑哄簱
+                task.setTaskType(TaskType.TASK_TYPE_OUT.type);
+            }
+            if (!taskService.save(task)) {
+                throw new CoolException("浠诲姟鍒涘缓澶辫触锛侊紒");
+            }
+            List<TaskItem> taskItems = new ArrayList<>();
+            listMap.get(key).forEach(item -> {
+                TaskItem taskItem = new TaskItem();
+                BeanUtils.copyProperties(item, taskItem);
+                taskItem.setTaskId(task.getId())
+                        .setAnfme(item.getOutQty())
+                        .setBatch(item.getBatch())
+                        .setOrderType(OrderType.ORDER_OUT.type)
+                        .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type));
+                taskItems.add(taskItem);
+
+                Double qty = Math.round((item.getWorkQty() + item.getOutQty()) * 10000) / 10000.0;
+                LocItem locItem = locItemService.getById(item.getId());
+                if (Objects.isNull(locItem)) {
+                    throw new CoolException("搴撳瓨淇℃伅涓嶅瓨鍦紒");
+                }
+
+                if (locItem.getAnfme().compareTo(qty) < 0) {
+                    Double minusQty = Math.round((locItem.getAnfme() - locItem.getWorkQty()) * 10000) / 10000.0;
+                    item.setWorkQty(minusQty);
+                } else {
+                    item.setWorkQty(qty);
+                }
+                if (! locItemService.updateById(item)) {
+                    throw new CoolException("搴撳瓨淇℃伅淇敼澶辫触锛侊紒");
+                }
+            });
+
+            if (!taskItemService.saveBatch(taskItems)) {
+                throw new CoolException("浠诲姟鏄庣粏鐢熸垚澶辫触锛侊紒");
+            }
+        });
+
+        return R.ok("浠诲姟鐢熸垚瀹屾垚锛�!");
+    }
 }

--
Gitblit v1.9.1