From d2ea33b96cb1394b5546b1bfc557c1c984e4bc5b Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期二, 22 四月 2025 12:36:50 +0800
Subject: [PATCH] 出库单明细功能优化

---
 rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx                                           |   10 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java     |  177 +++++++++++++++++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java              |   15 +
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java        |   43 +++-
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockItemService.java          |   22 ++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java    |   19 +-
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java              |    1 
 rsf-admin/src/page/orders/outStock/OutOrderList.jsx                                               |   55 ++++-
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java |  159 +++++++++++++++++
 9 files changed, 456 insertions(+), 45 deletions(-)

diff --git a/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
index 70a88a0..dea641b 100644
--- a/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
+++ b/rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
@@ -64,11 +64,11 @@
 const filters = [
   <SearchInput source="condition" alwaysOn />,
   <NumberInput source="asnId" label="table.field.outStockItem.asnId" />,
-  <TextInput source="asnCode" label="table.field.outStockItem.asnCode" />,
+  <TextInput source="asnCode" label="table.field.outStockItem.asnCode" alwaysOn/>,
   <TextInput source="poDetlId" label="table.field.outStockItem.poDetlId" />,
-  <TextInput source="poDetlCode" label="table.field.outStockItem.poDetlCode" />,
   <TextInput source="matnrId" label="table.field.outStockItem.matnrId" />,
-  <TextInput source="maktx" label="table.field.outStockItem.maktx" />,
+  <TextInput source="maktx" label="table.field.outStockItem.maktx" alwaysOn/>,
+  <TextInput source="matnrCode" label="table.field.outStockItem.matnrCode" alwaysOn/>,
   <NumberInput source="anfme" label="table.field.outStockItem.anfme" />,
   <TextInput source="stockUnit" label="table.field.outStockItem.stockUnit" />,
   <NumberInput source="purQty" label="table.field.outStockItem.purQty" />,
@@ -105,6 +105,7 @@
       <Box display="flex">
         <List
           resource="outStockItem"
+          storeKey='outStockItem'
           sx={{
             flexGrow: 1,
             transition: (theme) =>
@@ -114,7 +115,7 @@
             marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
           }}
           title={"menu.outStockItem"}
-          empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+          empty={false}
           filter={{ asnId: asnId, deleted: 0 }}
           filters={filters}
           sort={{ field: "create_time", order: "desc" }}
@@ -141,7 +142,6 @@
             <NumberField source="asnId" label="table.field.outStockItem.asnId" />
             <TextField source="asnCode" label="table.field.outStockItem.asnCode" />
             <TextField source="poDetlId" label="table.field.outStockItem.poDetlId" />
-            <TextField source="poDetlCode" label="table.field.outStockItem.poDetlCode" />
             <TextField source="matnrId" label="table.field.outStockItem.matnrId" />
             <TextField source="matnrCode" label="table.field.outStockItem.matnrCode" />
             <TextField source="maktx" label="table.field.outStockItem.maktx" />
diff --git a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
index f5b9dd5..dfff8b0 100644
--- a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
+++ b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -56,7 +56,7 @@
 import ExitToAppIcon from '@mui/icons-material/ExitToApp';
 import ImportButton from "../../components/ImportButton";
 import DetailsIcon from '@mui/icons-material/Details';
-
+import CancelIcon from '@mui/icons-material/Cancel';
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
   '& .css-1vooibu-MuiSvgIcon-root': {
     height: '.9em'
@@ -79,18 +79,20 @@
 
 const filters = [
   <SearchInput source="condition" alwaysOn />,
-  <TextInput source="code" label="table.field.asnOrder.code" />,
-  <TextInput source="poCode" label="table.field.asnOrder.poCode" />,
-  <NumberInput source="poId" label="table.field.asnOrder.poId" />,
-  <TextInput source="type" label="table.field.asnOrder.type" />,
-  <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type' }} label="table.field.asnOrder.wkType">
-    <AutocompleteInput label="table.field.asnOrder.wkType" optionValue="value" />
+  <TextInput source="code" label="table.field.outStock.code" alwaysOn />,
+  <TextInput source="poCode" label="table.field.outStock.poCode" />,
+  <NumberInput source="poId" label="table.field.outStock.poId" />,
+  <ReferenceInput source="type" reference="dictData" filter={{ dictTypeCode: 'sys_business_type' }} label="table.field.outStock.type" alwaysOn>
+    <AutocompleteInput label="table.field.outStock.type" optionValue="value" />
   </ReferenceInput>,
-  <NumberInput source="anfme" label="table.field.asnOrder.anfme" />,
-  <NumberInput source="qty" label="table.field.asnOrder.qty" />,
-  <TextInput source="logisNo" label="table.field.asnOrder.logisNo" />,
-  <DateInput source="arrTime" label="table.field.asnOrder.arrTime" />,
-  <SelectInput source="rleStatus" label="table.field.asnOrder.rleStatus"
+  <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type' }} label="table.field.outStock.wkType" alwaysOn>
+    <AutocompleteInput label="table.field.outStock.wkType" optionValue="value" />
+  </ReferenceInput>,
+  <NumberInput source="anfme" label="table.field.outStock.anfme" />,
+  <NumberInput source="qty" label="table.field.outStock.qty" />,
+  <TextInput source="logisNo" label="table.field.outStock.logisNo" />,
+  <DateInput source="arrTime" label="table.field.outStock.arrTime" />,
+  <SelectInput source="rleStatus" label="table.field.outStock.rleStatus"
     choices={[
       { id: 0, name: ' 姝e父' },
       { id: 1, name: ' 宸查噴鏀�' },
@@ -99,7 +101,7 @@
 
   <TextInput label="common.field.memo" source="memo" />,
   <DictionarySelect
-    label='table.field.asnOrder.exceStatus'
+    label='table.field.outStock.exceStatus'
     name="exceStatus"
     dictTypeCode="sys_asn_exce_status"
     alwaysOn
@@ -131,14 +133,14 @@
         title={"menu.outStock"}
         empty={<EmptyData onClick={() => { setCreateDialog(true); setmodalType(0) }} />}
         filters={filters}
-        filter={{deleted: 0, type: 'out'}}
+        filter={{ deleted: 0, type: 'out' }}
         sort={{ field: "create_time", order: "desc" }}
         actions={(
           <TopToolbar>
             <FilterButton />
             <MyCreateButton onClick={() => { setCreateDialog(true); setmodalType(0) }} />
             <SelectColumnsButton preferenceKey='outStock' />
-            <ImportButton value={'asnOrderItem'}  />
+            <ImportButton value={'asnOrderItem'} />
             <MyExportButton />
           </TopToolbar>
         )}
@@ -177,6 +179,7 @@
           <WrapperField cellClassName="opt" label="common.field.opt" >
             <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
             <MyButton setCreateDialog={setCreateDialog} setmodalType={setmodalType} />
+            <CancelButton></CancelButton>
             {/* <CompleteButton /> */}
           </WrapperField>
         </StyledDatagrid>
@@ -242,12 +245,32 @@
   )
 }
 
+const CancelButton = () => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+
+  const cancelOrder = async () => {
+    const { data: { code, data, msg } } = await request.post(`/outStock/cancel/${record.id}`);
+    if (code === 200) {
+      notify(msg);
+      refresh()
+    } else {
+      notify(msg);
+    }
+  }
+
+  return (
+    <ConfirmButton label={"toolbar.cancel"}  startIcon={<CancelIcon />} onConfirm={cancelOrder} />
+  )
+}
+
 const CloseButton = () => {
   const record = useRecordContext();
   const notify = useNotify();
   const refresh = useRefresh();
   const requestClose = async () => {
-    const { data: { code, data, msg } } = await request.post(`/asnOrder/close/${record.id}`);
+    const { data: { code, data, msg } } = await request.post(`/outStock/close/${record.id}`);
 
     if (code === 200) {
       notify(msg);
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
index 73618e0..152d98d 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -19,6 +19,8 @@
 import com.vincent.rsf.server.manager.enums.AsnExceStatus;
 import com.vincent.rsf.server.manager.service.AsnOrderItemService;
 import com.vincent.rsf.server.manager.service.AsnOrderService;
+import com.vincent.rsf.server.manager.service.OutStockItemService;
+import com.vincent.rsf.server.manager.service.OutStockService;
 import com.vincent.rsf.server.system.constant.SerialRuleCode;
 import com.vincent.rsf.server.system.controller.BaseController;
 import com.vincent.rsf.server.system.utils.SerialRuleUtils;
@@ -36,35 +38,35 @@
 public class OutStockController extends BaseController {
 
     @Autowired
-    private AsnOrderService asnOrderService;
+    private OutStockService outStockService;
     @Autowired
-    private AsnOrderItemService asnOrderItemService;
+    private OutStockItemService outStockItemService;
 
     @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping("/outStock/page")
     public R page(@RequestBody Map<String, Object> map) {
         BaseParam baseParam = buildParam(map, BaseParam.class);
         PageParam<AsnOrder, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrder.class);
-        return R.ok().add(asnOrderService.page(pageParam, pageParam.buildWrapper(true)));
+        return R.ok().add(outStockService.page(pageParam, pageParam.buildWrapper(true)));
     }
 
     @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping("/outStock/list")
     public R list(@RequestBody Map<String, Object> map) {
-        return R.ok().add(asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getType, OrderType.ORDER_OUT.type)));
+        return R.ok().add(outStockService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getType, OrderType.ORDER_OUT.type)));
     }
 
     @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping({"/outStock/many/{ids}", "/asnOrders/many/{ids}"})
     public R many(@PathVariable Long[] ids) {
-        return R.ok().add(asnOrderService.listByIds(Arrays.asList(ids)));
+        return R.ok().add(outStockService.listByIds(Arrays.asList(ids)));
     }
 
-    @PreAuthorize("hasAuthority('manager:asnOrder:list')")
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
     @OperationLog("琛ㄥ崟鏌ヨ")
     @GetMapping("/outStock/{id}")
     public R get(@PathVariable("id") Long id) {
-        return R.ok().add(asnOrderService.getById(id));
+        return R.ok().add(outStockService.getById(id));
     }
 
     @PreAuthorize("hasAuthority('manager:outStock:save')")
@@ -78,7 +80,7 @@
             String code = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, asnOrder);
             asnOrder.setCode(code);
         }
-        if (!asnOrderService.save(asnOrder)) {
+        if (!outStockService.save(asnOrder)) {
             return R.error("Save Fail");
         }
         return R.ok("Save Success").add(asnOrder);
@@ -92,7 +94,7 @@
         asnOrder.setType(OrderType.ORDER_OUT.type)
                 .setUpdateBy(getLoginUserId())
                 .setUpdateTime(new Date());
-        if (!asnOrderService.updateById(asnOrder)) {
+        if (!outStockService.updateById(asnOrder)) {
             return R.error("Update Fail");
         }
         return R.ok("Update Success").add(asnOrder);
@@ -102,7 +104,7 @@
     @OperationLog("Delete 鍑哄簱鍗曟嵁")
     @PostMapping("/outStock/remove/{ids}")
     public R remove(@PathVariable Long[] ids) {
-        if (!asnOrderService.removeByIds(Arrays.asList(ids))) {
+        if (!outStockService.removeByIds(Arrays.asList(ids))) {
             return R.error("Delete Fail");
         }
         return R.ok("Delete Success").add(ids);
@@ -117,11 +119,22 @@
         if (!Cools.isEmpty(condition)) {
             wrapper.like(AsnOrder::getCode, condition);
         }
-        asnOrderService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
+        outStockService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                 item -> vos.add(new KeyValVo(item.getId(), item.getCode()))
         );
         return R.ok().add(vos);
     }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @ApiOperation("鍙栨秷鍑哄簱鍗曟嵁")
+    @GetMapping("/outStock/cancel/{id}")
+    public R cancel(@PathVariable String id) {
+        if (Objects.isNull(id)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.cancelOutOrder(id);
+    }
+
 
     @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping("/outStock/export")
@@ -131,16 +144,16 @@
         if (!Objects.isNull(map.get("ids"))) {
             List<Long> ids = JSONArray.parseArray(JSONObject.toJSONString(map.get("ids")), Long.class);
             if (!ids.isEmpty()) {
-                orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, ids));
+                orders = outStockService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, ids));
             } else {
-                orders = asnOrderService.list(new LambdaQueryWrapper<>());
+                orders = outStockService.list(new LambdaQueryWrapper<>());
             }
         } else {
-            orders = asnOrderService.list();
+            orders = outStockService.list();
         }
         List<AsnOrderTemplate> orderTemplates = new ArrayList<>();
         for (AsnOrder order : orders) {
-            List<AsnOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()));
+            List<AsnOrderItem> orderItems = outStockItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()));
             for (AsnOrderItem item : orderItems) {
                 if (Objects.isNull(item)) {
                     continue;
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java
index 7504205..629367b 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockItemController.java
@@ -19,6 +19,7 @@
 import com.vincent.rsf.server.manager.enums.CompanysType;
 import com.vincent.rsf.server.manager.service.AsnOrderItemService;
 import com.vincent.rsf.server.manager.service.CompanysService;
+import com.vincent.rsf.server.manager.service.OutStockItemService;
 import com.vincent.rsf.server.system.controller.BaseController;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -36,19 +37,19 @@
 public class OutStockItemController extends BaseController {
 
     @Autowired
-    private AsnOrderItemService asnOrderItemService;
+    private OutStockItemService asnOrderItemService;
 
     @Autowired
     private CompanysService companysService;
 
-    @PreAuthorize("hasAuthority('manager:outStockItem:list')")
-    @ApiOperation("鍒嗛〉鑾峰彇鍒楄〃")
-    @PostMapping("/outStockItem/page")
-    public R page(@RequestBody Map<String, Object> map) {
-        BaseParam baseParam = buildParam(map, BaseParam.class);
-        PageParam<AsnOrderItem, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrderItem.class);
-        return R.ok().add(asnOrderItemService.listByAsnId(pageParam, pageParam.buildWrapper(true)));
-    }
+//    @PreAuthorize("hasAuthority('manager:outStockItem:list')")
+//    @ApiOperation("鍒嗛〉鑾峰彇鍒楄〃")
+//    @PostMapping("/outStockItem/page")
+//    public R page(@RequestBody Map<String, Object> map) {
+//        BaseParam baseParam = buildParam(map, BaseParam.class);
+//        PageParam<AsnOrderItem, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrderItem.class);
+//        return R.ok().add(asnOrderItemService.listByAsnId(pageParam, pageParam.buildWrapper(true)));
+//    }
 
     @PreAuthorize("hasAuthority('manager:outStockItem:list')")
     @PostMapping("/outStockItem/list")
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java
index ee9376f..2132c30 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java
@@ -24,4 +24,5 @@
     R completeOrder(Long id, Long loginUserId);
 
     R closeOrder(Long id);
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockItemService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockItemService.java
new file mode 100644
index 0000000..3927235
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockItemService.java
@@ -0,0 +1,22 @@
+package com.vincent.rsf.server.manager.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.manager.entity.AsnOrderItem;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public interface OutStockItemService extends IService<AsnOrderItem> {
+
+    boolean fieldsSave(Map<String, Object> params);
+
+    R excelImport(MultipartFile file, HashMap<String, Object> hashMap, Long loginUserId) throws Exception;
+
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
new file mode 100644
index 0000000..ec3e03f
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -0,0 +1,15 @@
+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.controller.params.AsnOrderAndItemsParams;
+import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam;
+import com.vincent.rsf.server.manager.entity.AsnOrder;
+
+import java.util.List;
+import java.util.Map;
+
+public interface OutStockService extends IService<AsnOrder> {
+
+    R cancelOutOrder(String id);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java
new file mode 100644
index 0000000..819b08a
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockItemServiceImpl.java
@@ -0,0 +1,159 @@
+package com.vincent.rsf.server.manager.service.impl;
+
+import cn.afterturn.easypoi.excel.ExcelImportUtil;
+import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.server.api.entity.enums.OrderType;
+import com.vincent.rsf.server.api.entity.enums.OrderWorkType;
+import com.vincent.rsf.server.common.utils.CommonUtil;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
+import com.vincent.rsf.server.common.utils.FieldsUtils;
+import com.vincent.rsf.server.manager.entity.AsnOrder;
+import com.vincent.rsf.server.manager.entity.AsnOrderItem;
+import com.vincent.rsf.server.manager.entity.Matnr;
+import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
+import com.vincent.rsf.server.manager.mapper.AsnOrderItemMapper;
+import com.vincent.rsf.server.manager.service.MatnrService;
+import com.vincent.rsf.server.manager.service.OutStockItemService;
+import com.vincent.rsf.server.manager.service.OutStockService;
+import com.vincent.rsf.server.system.constant.SerialRuleCode;
+import com.vincent.rsf.server.system.utils.SerialRuleUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Ryan
+ * @version 1.0
+ * @title OutStockItemServiceImpl
+ * @description
+ * @create 2025/4/22 11:35
+ */
+@Service("outStockItemServiceImpl")
+public class OutStockItemServiceImpl extends ServiceImpl<AsnOrderItemMapper, AsnOrderItem> implements OutStockItemService {
+
+    @Autowired
+    private OutStockService outStockService;
+    @Autowired
+    private MatnrService matnrService;
+    @Autowired
+    private OutStockItemService outStockItemService;
+
+    /**
+     * @param
+     * @return
+     * @author Ryan
+     * @description ASN鏄庣粏鍗曟嵁淇濆瓨锛屽強鎵╁睍瀛楁淇濆瓨
+     * @time 2025/4/7 09:59
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean fieldsSave(Map<String, Object> params) {
+        AsnOrderItem asnOrderItem = JSONObject.parseObject(JSONObject.toJSONString(params), AsnOrderItem.class);
+        if (StringUtils.isBlank(asnOrderItem.getTrackCode())) {
+            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_LABEL_CODE, asnOrderItem);
+            asnOrderItem.setTrackCode(ruleCode).setBarcode(ruleCode);
+            ;
+        }
+        if (Objects.isNull(asnOrderItem.getAnfme()) || Double.compare(asnOrderItem.getAnfme(), 0.0) <= 0) {
+            throw new CoolException("璁″垝鏀惰揣鏁颁笉鑳戒负绌猴紒锛�");
+        }
+        //淇濆瓨鎵╁睍瀛楁
+        try {
+            String uuid16 = CommonUtil.randomUUID16();
+            Boolean fields = FieldsUtils.saveFields(params, uuid16);
+            if (fields) {
+                asnOrderItem.setFieldsIndex(uuid16);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        if (!this.saveOrUpdate(asnOrderItem)) {
+            throw new CoolException("鏀惰揣閫氱煡鍗曟槑缁嗕繚瀛樺け璐ワ紒锛�");
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R excelImport(MultipartFile file, HashMap<String, Object> hashMap, Long loginUserId) throws Exception {
+        ExcelImportResult result = ExcelImportUtil.importExcelMore(file.getInputStream(), AsnOrderTemplate.class, ExcelUtil.getDefaultImportParams());
+        if (result.getList().isEmpty()) {
+            throw new CoolException("鐗╂枡瀵煎叆澶辫触锛侊紒");
+        }
+        if (result.getList().isEmpty()) {
+            throw new CoolException("琛ㄦ牸鍐呭涓嶈兘涓虹┖锛侊紒");
+        }
+        List<AsnOrderTemplate> resultList = result.getList();
+        Map<String, List<AsnOrderTemplate>> listMap = resultList.stream().collect(Collectors.groupingBy(AsnOrderTemplate::getCode));
+        for (String key : listMap.keySet()) {
+            AsnOrderTemplate template = listMap.get(key).stream().findFirst().get();
+            AsnOrder asnOrder = outStockService.getOne(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getCode, template.getCode()));
+            if (Objects.isNull(asnOrder)) {
+                continue;
+            }
+            AsnOrder order = new AsnOrder();
+            order.setCode(template.getCode())
+                    .setPoCode(template.getPoCode())
+                    .setMemo(template.getMemo())
+                    .setUpdateBy(loginUserId)
+                    .setCreateBy(loginUserId)
+                    .setPoId(Long.parseLong(template.getPoId()))
+                    .setType(OrderType.getTypeVal(template.getType()))
+                    .setWkType(OrderWorkType.getWorkType(template.getWkType()));
+            if (!outStockService.save(order)) {
+                throw new CoolException("鍗曟嵁淇濆瓨澶辫触锛侊紒");
+            }
+            List<AsnOrderItem> items = new ArrayList<>();
+            for (AsnOrderTemplate orderTemplate : listMap.get(key)) {
+                AsnOrderItem orderItem = new AsnOrderItem();
+                Matnr matnr = null;
+                if (!Objects.isNull(orderTemplate.getMatnrCode()) || StringUtils.isNotBlank(orderTemplate.getMatnrCode())) {
+                    matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>()
+                            .eq(Matnr::getCode, orderTemplate.getMatnrCode()));
+                }
+                orderItem.setAsnId(order.getId())
+                        .setAsnCode(order.getCode())
+                        .setSplrBatch(orderTemplate.getSplrBatch())
+                        .setAnfme(Double.parseDouble(orderTemplate.getAnfme()))
+                        .setQty(Double.parseDouble(orderTemplate.getQty()))
+                        .setSplrName(orderTemplate.getSplrName())
+                        .setBarcode(orderTemplate.getBarcode())
+                        .setTrackCode(orderTemplate.getTrackCode())
+                        .setSplrCode(orderTemplate.getSplrCode())
+                        .setPoCode(orderTemplate.getPoCode())
+                        .setMaktx(orderTemplate.getMaktx())
+                        .setMatnrCode(orderTemplate.getMatnrCode())
+                        .setPurUnit(orderTemplate.getPurUnit())
+                        .setPurQty(Double.parseDouble(orderTemplate.getPurQty()));
+                if (!Objects.isNull(matnr)) {
+                    orderItem.setMaktx(matnr.getName()).setMatnrCode(matnr.getCode()).setMatnrId(matnr.getId());
+                }
+                items.add(orderItem);
+                if (!outStockItemService.saveBatch(items)) {
+                    throw new CoolException("鍗曟嵁鏄庣粏淇濆瓨澶辫触锛侊紒");
+                }
+            }
+            if (!items.isEmpty()) {
+                double qty = items.stream().mapToDouble(AsnOrderItem::getQty).sum();
+                double purQty = items.stream().mapToDouble(AsnOrderItem::getPurQty).sum();
+                if (!outStockService.update(new LambdaUpdateWrapper<AsnOrder>().set(AsnOrder::getQty, qty).set(AsnOrder::getAnfme, purQty).eq(AsnOrder::getId, order.getId()))) {
+                    throw new CoolException("鍗曟嵁鏁伴噺淇敼澶辫触锛侊紒");
+                }
+            }
+        }
+
+        return R.ok("鎿嶄綔鎴愬姛锛侊紒");
+    }
+
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
new file mode 100644
index 0000000..c9c49cd
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -0,0 +1,177 @@
+package com.vincent.rsf.server.manager.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.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.entity.dto.PoItemsDto;
+import com.vincent.rsf.server.api.service.ReceiveMsgService;
+import com.vincent.rsf.server.api.service.ReportMsgService;
+import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
+import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam;
+import com.vincent.rsf.server.manager.entity.AsnOrder;
+import com.vincent.rsf.server.manager.entity.AsnOrderItem;
+import com.vincent.rsf.server.manager.entity.AsnOrderItemLog;
+import com.vincent.rsf.server.manager.entity.AsnOrderLog;
+import com.vincent.rsf.server.manager.enums.AsnExceStatus;
+import com.vincent.rsf.server.manager.mapper.AsnOrderMapper;
+import com.vincent.rsf.server.manager.mapper.PurchaseMapper;
+import com.vincent.rsf.server.manager.service.*;
+import com.vincent.rsf.server.system.constant.SerialRuleCode;
+import com.vincent.rsf.server.system.mapper.SerialRuleMapper;
+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 org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Ryan
+ * @description
+ * @throws
+ * @return
+ * @time 2025/3/7 08:02
+ */
+@Service("outStockServiceImpl")
+public class OutStockServiceImpl extends ServiceImpl<AsnOrderMapper, AsnOrder> implements OutStockService {
+
+    @Autowired
+    private ReceiveMsgService receiveMsgService;
+    @Autowired
+    private ReportMsgService reportMsgService;
+    @Resource
+    private PurchaseMapper purchaseMapper;
+    @Autowired
+    private AsnOrderItemService asnOrderItemService;
+    @Autowired
+    private AsnOrderLogService asnOrderLogService;
+    @Autowired
+    private AsnOrderItemLogService asnOrderItemLogService;
+    @Resource
+    private SerialRuleMapper serialRuleMapper;
+
+    /**
+     * @author Ryan
+     * @description 鏇存柊鎴栦繚瀛樻槑缁�
+     * @param
+     * @return
+     * @time 2025/4/7 13:28
+     */
+    @Transactional(rollbackFor = Exception.class)
+    private void svaeOrUpdateOrderItem(AsnOrderAndItemsParams params, Long loginUserId) throws Exception{
+        AsnOrder orders = params.getOrders();
+
+        params.getItems().forEach(item -> {
+            item.put("asnId", orders.getId());
+            item.put("asnCode", orders.getCode());
+            item.put("poCode", orders.getPoCode());
+            item.put("createBy", loginUserId);
+            item.put("updateBy", loginUserId);
+            if (!asnOrderItemService.fieldsSave(item)) {
+                throw new CoolException("鏄庣粏淇濆瓨澶辫触锛侊紒");
+            }
+        });
+        List<AsnOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
+                .eq(AsnOrderItem::getAsnId, params.getOrders().getId()));
+        double sum = orderItems.stream().mapToDouble(AsnOrderItem::getAnfme).sum();
+        orders.setAnfme(sum);
+        if (!this.updateById(orders)) {
+            throw new CoolException("璁″垝鏀惰揣鏁伴噺淇敼澶辫触锛侊紒");
+        }
+    }
+
+
+    /**
+     * @author Ryan
+     * @description 鍒犻櫎鍘熶富鍗曞強鏄庣粏锛屽姞鍏ュ巻鍙叉。
+     * @param
+     * @return
+     * @time 2025/3/19 19:53
+     */
+    @Transactional(rollbackFor = Exception.class)
+    private void operateOrderLogs(AsnOrder asrder) throws Exception{
+        if (Objects.isNull(asrder) || Objects.isNull(asrder.getId())) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        asrder.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val);
+
+        if (!this.updateById(asrder)) {
+            throw new CoolException("鍗曟嵁鍏抽棴澶辫触锛侊紒");
+        }
+        List<AsnOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, asrder.getId()));
+        if (orderItems.isEmpty()) {
+            throw new CoolException("鏀惰揣鏄庣粏涓虹┖锛侊紒");
+        }
+//        if (Objects.isNull(asrder.getAnfme()) || asrder.getAnfme().compareTo(0.00) == 0) {
+//            throw new CoolException("鏀惰揣鏁伴噺涓嶈兘涓洪浂锛侊紒");
+//        }
+        AsnOrder order = this.getById(asrder.getId());
+        AsnOrderLog orderLog = new AsnOrderLog();
+        order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_DONE.val);
+        BeanUtils.copyProperties(order, orderLog);
+        orderLog.setId(null);
+        orderLog.setAsnId(order.getId());
+
+        if (!this.saveOrUpdate(order)) {
+            throw new CoolException("鐘舵�佷慨鏀瑰け璐ワ紒锛�");
+        }
+        orderLog.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val);
+        if (!asnOrderLogService.save(orderLog)) {
+            throw new CoolException("涓诲崟鍘嗗彶妗f坊鍔犲け璐ワ紒锛�");
+        }
+        List<AsnOrderItemLog> logs = new ArrayList<>();
+        List<AsnOrderItem> items = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()));
+        items.forEach(item -> {
+            AsnOrderItemLog itemLog = new AsnOrderItemLog();
+            BeanUtils.copyProperties(item, itemLog);
+            itemLog.setAsnItemId(itemLog.getId())
+                    .setLogId(orderLog.getId())
+                    .setAsnId(item.getAsnId());
+            logs.add(itemLog);
+        });
+
+        if (!asnOrderItemLogService.saveBatch(logs)) {
+            throw new CoolException("閫氱煡鍗曟槑缁嗗巻鍙叉。淇濆瓨澶辫触锛侊紒");
+        }
+        if (!asnOrderItemService.remove(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()))) {
+            throw new CoolException("鍘熷崟鎹槑缁嗗垹闄ゅけ璐ワ紒锛�");
+        }
+        if (!this.removeById(asrder.getId())) {
+            throw new CoolException("鍘熷崟鎹垹闄ゅけ璐ワ紒锛�");
+        }
+    }
+
+    /**
+     * @author Ryan
+     * @description 鍙栨秷鍑哄簱鍗曟嵁
+     * @param
+     * @return
+     * @time 2025/4/22 10:40
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R cancelOutOrder(String id) {
+        if (Cools.isEmpty(id)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        AsnOrder order = this.getById(id);
+        if (Objects.isNull(order)) {
+            throw new CoolException("鍗曟嵁涓嶅瓨鍦紒锛�");
+        }
+        if (!order.getExceStatus().equals(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)) {
+            throw new CoolException("褰撳墠鍗曟嵁鐘舵�佷负:" + AsnExceStatus.getExceStatus(order.getExceStatus()) + "锛� 涓嶅彲鎵ц鍙栨秷鎿嶄綔锛侊紒");
+        }
+        order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_CANCEL.val).setStatus(0);
+
+        if (!this.saveOrUpdate(order)) {
+            throw new CoolException("鍗曟嵁鍙栨秷澶辫触锛侊紒");
+        }
+        return R.ok("鎿嶄綔鎴愬姛");
+    }
+}

--
Gitblit v1.9.1