Ver Fonte

pms SAP库龄数据 查询帆软数仓

zhangcl há 1 semana atrás
pai
commit
2a8f7efa18
25 ficheiros alterados com 2427 adições e 1 exclusões
  1. 4 0
      yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java
  2. 1 1
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/hikvision/vo/MonitorPointsPageReqVO.java
  3. 93 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/IotProdInventoryAgingController.java
  4. 98 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingPageReqVO.java
  5. 127 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingRespVO.java
  6. 96 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingSaveReqVO.java
  7. 93 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/IotSapInventoryAgeController.java
  8. 149 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgePageReqVO.java
  9. 188 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgeRespVO.java
  10. 139 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgeSaveReqVO.java
  11. 135 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotprodinventoryaging/IotProdInventoryAgingDO.java
  12. 201 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotsapinventoryage/IotSapInventoryAgeDO.java
  13. 53 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprodinventoryaging/IotProdInventoryAgingMapper.java
  14. 65 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotsapinventoryage/IotSapInventoryAgeMapper.java
  15. 325 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/sap/SyncSapInventoryAgeJob.java
  16. 72 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/sap/SyncSapStockAgeContext.java
  17. 79 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/SapController.java
  18. 7 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/service/IotSapService.java
  19. 155 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/service/IotSapServiceImpl.java
  20. 89 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/vo/IotSapInventoryAgeVO.java
  21. 55 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingService.java
  22. 71 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingServiceImpl.java
  23. 55 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotsapinventoryage/IotSapInventoryAgeService.java
  24. 72 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotsapinventoryage/IotSapInventoryAgeServiceImpl.java
  25. 5 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/saporg/dto/SapOrgRespDTO.java

+ 4 - 0
yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java

@@ -122,6 +122,10 @@ public interface ErrorCodeConstant{
     ErrorCode IOT_PROJECT_TASK_PLAN_NOT_EXISTS = new ErrorCode(700, "日报任务计划(工作量)不存在");
     ErrorCode IOT_PROJECT_TASK_PLAN_EXISTS = new ErrorCode(700, "部门当前月份的工作量计划已经存在");
     ErrorCode IOT_PRODUCTION_EXCEPTION_NOT_EXISTS = new ErrorCode(800, "生产异常日志不存在");
+
+    ErrorCode IOT_SAP_INVENTORY_AGE_NOT_EXISTS = new ErrorCode(900, "PMS SAP 库龄不存在");
+
+    ErrorCode IOT_PROD_INVENTORY_AGING_NOT_EXISTS = new ErrorCode(901, "库存库龄表DWD不存在");
     /***
      * 数字油藏
      */

+ 1 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/hikvision/vo/MonitorPointsPageReqVO.java → yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/hikvision/vo/MonitorPointsPageReqVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.pms.hikvision.vo;
+package cn.iocoder.yudao.module.pms.controller.admin.hikvision.vo;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import io.swagger.v3.oas.annotations.media.Schema;

+ 93 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/IotProdInventoryAgingController.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
+import cn.iocoder.yudao.module.pms.service.iotprodinventoryaging.IotProdInventoryAgingService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 库存库龄表DWD")
+@RestController
+@RequestMapping("/pms/iot-prod-inventory-aging")
+@Validated
+public class IotProdInventoryAgingController {
+
+    @Resource
+    private IotProdInventoryAgingService iotProdInventoryAgingService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建库存库龄表DWD")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:create')")
+    public CommonResult<Long> createIotProdInventoryAging(@Valid @RequestBody IotProdInventoryAgingSaveReqVO createReqVO) {
+        return success(iotProdInventoryAgingService.createIotProdInventoryAging(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新库存库龄表DWD")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:update')")
+    public CommonResult<Boolean> updateIotProdInventoryAging(@Valid @RequestBody IotProdInventoryAgingSaveReqVO updateReqVO) {
+        iotProdInventoryAgingService.updateIotProdInventoryAging(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除库存库龄表DWD")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:delete')")
+    public CommonResult<Boolean> deleteIotProdInventoryAging(@RequestParam("id") Long id) {
+        iotProdInventoryAgingService.deleteIotProdInventoryAging(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得库存库龄表DWD")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<IotProdInventoryAgingRespVO> getIotProdInventoryAging(@RequestParam("id") Long id) {
+        IotProdInventoryAgingDO iotProdInventoryAging = iotProdInventoryAgingService.getIotProdInventoryAging(id);
+        return success(BeanUtils.toBean(iotProdInventoryAging, IotProdInventoryAgingRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得库存库龄表DWD分页")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<PageResult<IotProdInventoryAgingRespVO>> getIotProdInventoryAgingPage(@Valid IotProdInventoryAgingPageReqVO pageReqVO) {
+        PageResult<IotProdInventoryAgingDO> pageResult = iotProdInventoryAgingService.getIotProdInventoryAgingPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, IotProdInventoryAgingRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出库存库龄表DWD Excel")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportIotProdInventoryAgingExcel(@Valid IotProdInventoryAgingPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<IotProdInventoryAgingDO> list = iotProdInventoryAgingService.getIotProdInventoryAgingPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "库存库龄表DWD.xls", "数据", IotProdInventoryAgingRespVO.class,
+                        BeanUtils.toBean(list, IotProdInventoryAgingRespVO.class));
+    }
+
+}

+ 98 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingPageReqVO.java

@@ -0,0 +1,98 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 库存库龄表DWD分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class IotProdInventoryAgingPageReqVO extends PageParam {
+
+    @Schema(description = "财年")
+    private String fyear;
+
+    @Schema(description = "期间")
+    private String fmonth;
+
+    @Schema(description = "日期")
+    private String fdate;
+
+    @Schema(description = "工厂")
+    private String werks;
+
+    @Schema(description = "部门")
+    private String dept;
+
+    @Schema(description = "物料")
+    private String matnr;
+
+    @Schema(description = "库存地点")
+    private String lgort;
+
+    @Schema(description = "评估类")
+    private String bklas;
+
+    @Schema(description = "物料组")
+    private String matkl;
+
+    @Schema(description = "物料类型")
+    private String mtart;
+
+    @Schema(description = "物料描述")
+    private String maktx;
+
+    @Schema(description = "销售凭证")
+    private String vbeln;
+
+    @Schema(description = "销售项目")
+    private String posnr;
+
+    @Schema(description = "项目描述")
+    private String bezei;
+
+    @Schema(description = "总库存")
+    private BigDecimal total;
+
+    @Schema(description = "标准价格(元)")
+    private BigDecimal stprs;
+
+    @Schema(description = "行总库存成本(元)")
+    private BigDecimal amttotal;
+
+    @Schema(description = "价格单位")
+    private BigDecimal peinh;
+
+    @Schema(description = "库龄段1")
+    private BigDecimal qty1;
+
+    @Schema(description = "库龄段2")
+    private BigDecimal qty2;
+
+    @Schema(description = "库龄段3")
+    private BigDecimal qty3;
+
+    @Schema(description = "库龄段4")
+    private BigDecimal qty4;
+
+    @Schema(description = "库龄段1成本")
+    private BigDecimal amt1;
+
+    @Schema(description = "库龄段2成本")
+    private BigDecimal amt2;
+
+    @Schema(description = "库龄段3成本")
+    private BigDecimal amt3;
+
+    @Schema(description = "库龄段4成本")
+    private BigDecimal amt4;
+
+    @Schema(description = "库龄段其它成本")
+    private BigDecimal amtn;
+
+}

+ 127 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingRespVO.java

@@ -0,0 +1,127 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 库存库龄表DWD Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotProdInventoryAgingRespVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31724")
+    @ExcelProperty("主键")
+    private Long id;
+
+    @Schema(description = "财年")
+    @ExcelProperty("财年")
+    private String fyear;
+
+    @Schema(description = "期间")
+    @ExcelProperty("期间")
+    private String fmonth;
+
+    @Schema(description = "日期")
+    @ExcelProperty("日期")
+    private String fdate;
+
+    @Schema(description = "工厂")
+    @ExcelProperty("工厂")
+    private String werks;
+
+    @Schema(description = "部门")
+    @ExcelProperty("部门")
+    private String dept;
+
+    @Schema(description = "物料")
+    @ExcelProperty("物料")
+    private String matnr;
+
+    @Schema(description = "库存地点")
+    @ExcelProperty("库存地点")
+    private String lgort;
+
+    @Schema(description = "评估类")
+    @ExcelProperty("评估类")
+    private String bklas;
+
+    @Schema(description = "物料组")
+    @ExcelProperty("物料组")
+    private String matkl;
+
+    @Schema(description = "物料类型")
+    @ExcelProperty("物料类型")
+    private String mtart;
+
+    @Schema(description = "物料描述")
+    @ExcelProperty("物料描述")
+    private String maktx;
+
+    @Schema(description = "销售凭证")
+    @ExcelProperty("销售凭证")
+    private String vbeln;
+
+    @Schema(description = "销售项目")
+    @ExcelProperty("销售项目")
+    private String posnr;
+
+    @Schema(description = "项目描述")
+    @ExcelProperty("项目描述")
+    private String bezei;
+
+    @Schema(description = "总库存")
+    @ExcelProperty("总库存")
+    private BigDecimal total;
+
+    @Schema(description = "标准价格(元)")
+    @ExcelProperty("标准价格(元)")
+    private BigDecimal stprs;
+
+    @Schema(description = "行总库存成本(元)")
+    @ExcelProperty("行总库存成本(元)")
+    private BigDecimal amttotal;
+
+    @Schema(description = "价格单位")
+    @ExcelProperty("价格单位")
+    private BigDecimal peinh;
+
+    @Schema(description = "库龄段1")
+    @ExcelProperty("库龄段1")
+    private BigDecimal qty1;
+
+    @Schema(description = "库龄段2")
+    @ExcelProperty("库龄段2")
+    private BigDecimal qty2;
+
+    @Schema(description = "库龄段3")
+    @ExcelProperty("库龄段3")
+    private BigDecimal qty3;
+
+    @Schema(description = "库龄段4")
+    @ExcelProperty("库龄段4")
+    private BigDecimal qty4;
+
+    @Schema(description = "库龄段1成本")
+    @ExcelProperty("库龄段1成本")
+    private BigDecimal amt1;
+
+    @Schema(description = "库龄段2成本")
+    @ExcelProperty("库龄段2成本")
+    private BigDecimal amt2;
+
+    @Schema(description = "库龄段3成本")
+    @ExcelProperty("库龄段3成本")
+    private BigDecimal amt3;
+
+    @Schema(description = "库龄段4成本")
+    @ExcelProperty("库龄段4成本")
+    private BigDecimal amt4;
+
+    @Schema(description = "库龄段其它成本")
+    @ExcelProperty("库龄段其它成本")
+    private BigDecimal amtn;
+
+}

+ 96 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotProdInventoryAgingSaveReqVO.java

@@ -0,0 +1,96 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 库存库龄表DWD新增/修改 Request VO")
+@Data
+public class IotProdInventoryAgingSaveReqVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31724")
+    private Long id;
+
+    @Schema(description = "财年")
+    private String fyear;
+
+    @Schema(description = "期间")
+    private String fmonth;
+
+    @Schema(description = "日期")
+    private String fdate;
+
+    @Schema(description = "工厂")
+    private String werks;
+
+    @Schema(description = "部门")
+    private String dept;
+
+    @Schema(description = "物料")
+    private String matnr;
+
+    @Schema(description = "库存地点")
+    private String lgort;
+
+    @Schema(description = "评估类")
+    private String bklas;
+
+    @Schema(description = "物料组")
+    private String matkl;
+
+    @Schema(description = "物料类型")
+    private String mtart;
+
+    @Schema(description = "物料描述")
+    private String maktx;
+
+    @Schema(description = "销售凭证")
+    private String vbeln;
+
+    @Schema(description = "销售项目")
+    private String posnr;
+
+    @Schema(description = "项目描述")
+    private String bezei;
+
+    @Schema(description = "总库存")
+    private BigDecimal total;
+
+    @Schema(description = "标准价格(元)")
+    private BigDecimal stprs;
+
+    @Schema(description = "行总库存成本(元)")
+    private BigDecimal amttotal;
+
+    @Schema(description = "价格单位")
+    private BigDecimal peinh;
+
+    @Schema(description = "库龄段1")
+    private BigDecimal qty1;
+
+    @Schema(description = "库龄段2")
+    private BigDecimal qty2;
+
+    @Schema(description = "库龄段3")
+    private BigDecimal qty3;
+
+    @Schema(description = "库龄段4")
+    private BigDecimal qty4;
+
+    @Schema(description = "库龄段1成本")
+    private BigDecimal amt1;
+
+    @Schema(description = "库龄段2成本")
+    private BigDecimal amt2;
+
+    @Schema(description = "库龄段3成本")
+    private BigDecimal amt3;
+
+    @Schema(description = "库龄段4成本")
+    private BigDecimal amt4;
+
+    @Schema(description = "库龄段其它成本")
+    private BigDecimal amtn;
+
+}

+ 93 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/IotSapInventoryAgeController.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgePageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgeRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgeSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
+import cn.iocoder.yudao.module.pms.service.iotsapinventoryage.IotSapInventoryAgeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - PMS SAP 库龄")
+@RestController
+@RequestMapping("/pms/iot-sap-inventory-age")
+@Validated
+public class IotSapInventoryAgeController {
+
+    @Resource
+    private IotSapInventoryAgeService iotSapInventoryAgeService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建PMS SAP 库龄")
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:create')")
+    public CommonResult<Long> createIotSapInventoryAge(@Valid @RequestBody IotSapInventoryAgeSaveReqVO createReqVO) {
+        return success(iotSapInventoryAgeService.createIotSapInventoryAge(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新PMS SAP 库龄")
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:update')")
+    public CommonResult<Boolean> updateIotSapInventoryAge(@Valid @RequestBody IotSapInventoryAgeSaveReqVO updateReqVO) {
+        iotSapInventoryAgeService.updateIotSapInventoryAge(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除PMS SAP 库龄")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:delete')")
+    public CommonResult<Boolean> deleteIotSapInventoryAge(@RequestParam("id") Long id) {
+        iotSapInventoryAgeService.deleteIotSapInventoryAge(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得PMS SAP 库龄")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:query')")
+    public CommonResult<IotSapInventoryAgeRespVO> getIotSapInventoryAge(@RequestParam("id") Long id) {
+        IotSapInventoryAgeDO iotSapInventoryAge = iotSapInventoryAgeService.getIotSapInventoryAge(id);
+        return success(BeanUtils.toBean(iotSapInventoryAge, IotSapInventoryAgeRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得PMS SAP 库龄分页")
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:query')")
+    public CommonResult<PageResult<IotSapInventoryAgeRespVO>> getIotSapInventoryAgePage(@Valid IotSapInventoryAgePageReqVO pageReqVO) {
+        PageResult<IotSapInventoryAgeDO> pageResult = iotSapInventoryAgeService.getIotSapInventoryAgePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, IotSapInventoryAgeRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出PMS SAP 库龄 Excel")
+    @PreAuthorize("@ss.hasPermission('pms:iot-sap-inventory-age:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportIotSapInventoryAgeExcel(@Valid IotSapInventoryAgePageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<IotSapInventoryAgeDO> list = iotSapInventoryAgeService.getIotSapInventoryAgePage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "PMS SAP 库龄.xls", "数据", IotSapInventoryAgeRespVO.class,
+                        BeanUtils.toBean(list, IotSapInventoryAgeRespVO.class));
+    }
+
+}

+ 149 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgePageReqVO.java

@@ -0,0 +1,149 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - PMS SAP 库龄分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class IotSapInventoryAgePageReqVO extends PageParam {
+
+    @Schema(description = "组织部门id", example = "15981")
+    private Long deptId;
+
+    @Schema(description = "sap工厂编号")
+    private String factoryCode;
+
+    @Schema(description = "工厂id (system_sap_org表id)", example = "19919")
+    private Long factoryId;
+
+    @Schema(description = "工厂(SAP) 专业公司")
+    private String factory;
+
+    @Schema(description = "SAP库存地点编码")
+    private String storageLocationCode;
+
+    @Schema(description = "库存地点id", example = "25597")
+    private Long storageLocationId;
+
+    @Schema(description = "库存地点 - SAP 部门 项目部级 通用库 项目部库")
+    private String projectDepartment;
+
+    @Schema(description = "物料编码")
+    private String materialCode;
+
+    @Schema(description = "物料名称", example = "芋艿")
+    private String materialName;
+
+    @Schema(description = "物料组", example = "张三")
+    private String materialGroupName;
+
+    @Schema(description = "物料组id", example = "32217")
+    private Long materialGroupId;
+
+    @Schema(description = "数量")
+    private BigDecimal quantity;
+
+    @Schema(description = "单价", example = "29013")
+    private BigDecimal unitPrice;
+
+    @Schema(description = "总价", example = "846")
+    private BigDecimal totalPrice;
+
+    @Schema(description = "评估类")
+    private String valuationClass;
+
+    @Schema(description = "币种")
+    private String currency;
+
+    @Schema(description = "财年")
+    private String fiscalYear;
+
+    @Schema(description = "期间")
+    private String period;
+
+    @Schema(description = "物料类型", example = "1")
+    private String materialType;
+
+    @Schema(description = "物料类型描述", example = "随便")
+    private String description;
+
+    @Schema(description = "销售和分销凭证")
+    private String salesDocument;
+
+    @Schema(description = "项目")
+    private String project;
+
+    @Schema(description = "项目描述")
+    private String comment;
+
+    @Schema(description = "非限制")
+    private BigDecimal unrestrictedUse;
+
+    @Schema(description = "标准价格", example = "17360")
+    private BigDecimal standardPrice;
+
+    @Schema(description = "行总库存成本")
+    private BigDecimal lineTotalInventoryCost;
+
+    @Schema(description = "价格单位")
+    private BigDecimal priceUnit;
+
+    @Schema(description = "库龄段1")
+    private BigDecimal agingBandOne;
+
+    @Schema(description = "库龄段2")
+    private BigDecimal agingBandTwo;
+
+    @Schema(description = "库龄段3")
+    private BigDecimal agingBandThree;
+
+    @Schema(description = "库龄段4")
+    private BigDecimal agingBandFour;
+
+    @Schema(description = "库龄段1成本")
+    private BigDecimal agingBandOneCost;
+
+    @Schema(description = "库龄段2成本")
+    private BigDecimal agingBandTwoCost;
+
+    @Schema(description = "库龄段3成本")
+    private BigDecimal agingBandThreeCost;
+
+    @Schema(description = "库龄段4成本")
+    private BigDecimal agingBandFourCost;
+
+    @Schema(description = "同步状态 0未同步 1同步成功 2同步失败", example = "1")
+    private Integer syncStatus;
+
+    @Schema(description = "最后一次同步时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] syncTime;
+
+    @Schema(description = "同步失败时的错误信息")
+    private String syncError;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "状态 0启用  1停用", example = "2")
+    private Integer status;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 188 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgeRespVO.java

@@ -0,0 +1,188 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - PMS SAP 库龄 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotSapInventoryAgeRespVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "11496")
+    @ExcelProperty("主键")
+    private Long id;
+
+    @Schema(description = "组织部门id", example = "15981")
+    @ExcelProperty("组织部门id")
+    private Long deptId;
+
+    @Schema(description = "sap工厂编号")
+    @ExcelProperty("sap工厂编号")
+    private String factoryCode;
+
+    @Schema(description = "工厂id (system_sap_org表id)", example = "19919")
+    @ExcelProperty("工厂id (system_sap_org表id)")
+    private Long factoryId;
+
+    @Schema(description = "工厂(SAP) 专业公司")
+    @ExcelProperty("工厂(SAP) 专业公司")
+    private String factory;
+
+    @Schema(description = "SAP库存地点编码")
+    @ExcelProperty("SAP库存地点编码")
+    private String storageLocationCode;
+
+    @Schema(description = "库存地点id", example = "25597")
+    @ExcelProperty("库存地点id")
+    private Long storageLocationId;
+
+    @Schema(description = "库存地点 - SAP 部门 项目部级 通用库 项目部库")
+    @ExcelProperty("库存地点 - SAP 部门 项目部级 通用库 项目部库")
+    private String projectDepartment;
+
+    @Schema(description = "物料编码")
+    @ExcelProperty("物料编码")
+    private String materialCode;
+
+    @Schema(description = "物料名称", example = "芋艿")
+    @ExcelProperty("物料名称")
+    private String materialName;
+
+    @Schema(description = "物料组", example = "张三")
+    @ExcelProperty("物料组")
+    private String materialGroupName;
+
+    @Schema(description = "物料组id", example = "32217")
+    @ExcelProperty("物料组id")
+    private Long materialGroupId;
+
+    @Schema(description = "数量")
+    @ExcelProperty("数量")
+    private BigDecimal quantity;
+
+    @Schema(description = "单价", example = "29013")
+    @ExcelProperty("单价")
+    private BigDecimal unitPrice;
+
+    @Schema(description = "总价", example = "846")
+    @ExcelProperty("总价")
+    private BigDecimal totalPrice;
+
+    @Schema(description = "评估类")
+    @ExcelProperty("评估类")
+    private String valuationClass;
+
+    @Schema(description = "币种")
+    @ExcelProperty("币种")
+    private String currency;
+
+    @Schema(description = "财年")
+    @ExcelProperty("财年")
+    private String fiscalYear;
+
+    @Schema(description = "期间")
+    @ExcelProperty("期间")
+    private String period;
+
+    @Schema(description = "物料类型", example = "1")
+    @ExcelProperty("物料类型")
+    private String materialType;
+
+    @Schema(description = "物料类型描述", example = "随便")
+    @ExcelProperty("物料类型描述")
+    private String description;
+
+    @Schema(description = "销售和分销凭证")
+    @ExcelProperty("销售和分销凭证")
+    private String salesDocument;
+
+    @Schema(description = "项目")
+    @ExcelProperty("项目")
+    private String project;
+
+    @Schema(description = "项目描述")
+    @ExcelProperty("项目描述")
+    private String comment;
+
+    @Schema(description = "非限制")
+    @ExcelProperty("非限制")
+    private BigDecimal unrestrictedUse;
+
+    @Schema(description = "标准价格", example = "17360")
+    @ExcelProperty("标准价格")
+    private BigDecimal standardPrice;
+
+    @Schema(description = "行总库存成本")
+    @ExcelProperty("行总库存成本")
+    private BigDecimal lineTotalInventoryCost;
+
+    @Schema(description = "价格单位")
+    @ExcelProperty("价格单位")
+    private BigDecimal priceUnit;
+
+    @Schema(description = "库龄段1")
+    @ExcelProperty("库龄段1")
+    private BigDecimal agingBandOne;
+
+    @Schema(description = "库龄段2")
+    @ExcelProperty("库龄段2")
+    private BigDecimal agingBandTwo;
+
+    @Schema(description = "库龄段3")
+    @ExcelProperty("库龄段3")
+    private BigDecimal agingBandThree;
+
+    @Schema(description = "库龄段4")
+    @ExcelProperty("库龄段4")
+    private BigDecimal agingBandFour;
+
+    @Schema(description = "库龄段1成本")
+    @ExcelProperty("库龄段1成本")
+    private BigDecimal agingBandOneCost;
+
+    @Schema(description = "库龄段2成本")
+    @ExcelProperty("库龄段2成本")
+    private BigDecimal agingBandTwoCost;
+
+    @Schema(description = "库龄段3成本")
+    @ExcelProperty("库龄段3成本")
+    private BigDecimal agingBandThreeCost;
+
+    @Schema(description = "库龄段4成本")
+    @ExcelProperty("库龄段4成本")
+    private BigDecimal agingBandFourCost;
+
+    @Schema(description = "同步状态 0未同步 1同步成功 2同步失败", example = "1")
+    @ExcelProperty("同步状态 0未同步 1同步成功 2同步失败")
+    private Integer syncStatus;
+
+    @Schema(description = "最后一次同步时间")
+    @ExcelProperty("最后一次同步时间")
+    private LocalDateTime syncTime;
+
+    @Schema(description = "同步失败时的错误信息")
+    @ExcelProperty("同步失败时的错误信息")
+    private String syncError;
+
+    @Schema(description = "排序")
+    @ExcelProperty("排序")
+    private Integer sort;
+
+    @Schema(description = "状态 0启用  1停用", example = "2")
+    @ExcelProperty("状态 0启用  1停用")
+    private Integer status;
+
+    @Schema(description = "备注", example = "你说的对")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 139 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotsapinventoryage/vo/IotSapInventoryAgeSaveReqVO.java

@@ -0,0 +1,139 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - PMS SAP 库龄新增/修改 Request VO")
+@Data
+public class IotSapInventoryAgeSaveReqVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "11496")
+    private Long id;
+
+    @Schema(description = "组织部门id", example = "15981")
+    private Long deptId;
+
+    @Schema(description = "sap工厂编号")
+    private String factoryCode;
+
+    @Schema(description = "工厂id (system_sap_org表id)", example = "19919")
+    private Long factoryId;
+
+    @Schema(description = "工厂(SAP) 专业公司")
+    private String factory;
+
+    @Schema(description = "SAP库存地点编码")
+    private String storageLocationCode;
+
+    @Schema(description = "库存地点id", example = "25597")
+    private Long storageLocationId;
+
+    @Schema(description = "库存地点 - SAP 部门 项目部级 通用库 项目部库")
+    private String projectDepartment;
+
+    @Schema(description = "物料编码")
+    private String materialCode;
+
+    @Schema(description = "物料名称", example = "芋艿")
+    private String materialName;
+
+    @Schema(description = "物料组", example = "张三")
+    private String materialGroupName;
+
+    @Schema(description = "物料组id", example = "32217")
+    private Long materialGroupId;
+
+    @Schema(description = "数量")
+    private BigDecimal quantity;
+
+    @Schema(description = "单价", example = "29013")
+    private BigDecimal unitPrice;
+
+    @Schema(description = "总价", example = "846")
+    private BigDecimal totalPrice;
+
+    @Schema(description = "评估类")
+    private String valuationClass;
+
+    @Schema(description = "币种")
+    private String currency;
+
+    @Schema(description = "财年")
+    private String fiscalYear;
+
+    @Schema(description = "期间")
+    private String period;
+
+    @Schema(description = "物料类型", example = "1")
+    private String materialType;
+
+    @Schema(description = "物料类型描述", example = "随便")
+    private String description;
+
+    @Schema(description = "销售和分销凭证")
+    private String salesDocument;
+
+    @Schema(description = "项目")
+    private String project;
+
+    @Schema(description = "项目描述")
+    private String comment;
+
+    @Schema(description = "非限制")
+    private BigDecimal unrestrictedUse;
+
+    @Schema(description = "标准价格", example = "17360")
+    private BigDecimal standardPrice;
+
+    @Schema(description = "行总库存成本")
+    private BigDecimal lineTotalInventoryCost;
+
+    @Schema(description = "价格单位")
+    private BigDecimal priceUnit;
+
+    @Schema(description = "库龄段1")
+    private BigDecimal agingBandOne;
+
+    @Schema(description = "库龄段2")
+    private BigDecimal agingBandTwo;
+
+    @Schema(description = "库龄段3")
+    private BigDecimal agingBandThree;
+
+    @Schema(description = "库龄段4")
+    private BigDecimal agingBandFour;
+
+    @Schema(description = "库龄段1成本")
+    private BigDecimal agingBandOneCost;
+
+    @Schema(description = "库龄段2成本")
+    private BigDecimal agingBandTwoCost;
+
+    @Schema(description = "库龄段3成本")
+    private BigDecimal agingBandThreeCost;
+
+    @Schema(description = "库龄段4成本")
+    private BigDecimal agingBandFourCost;
+
+    @Schema(description = "同步状态 0未同步 1同步成功 2同步失败", example = "1")
+    private Integer syncStatus;
+
+    @Schema(description = "最后一次同步时间")
+    private LocalDateTime syncTime;
+
+    @Schema(description = "同步失败时的错误信息")
+    private String syncError;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "状态 0启用  1停用", example = "2")
+    private Integer status;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+}

+ 135 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotprodinventoryaging/IotProdInventoryAgingDO.java

@@ -0,0 +1,135 @@
+package cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fhs.core.trans.vo.TransPojo;
+import lombok.*;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 库存库龄表DWD DO
+ *
+ * @author ruiqi
+ */
+@TableName("dwd_prod_inventory_aging")
+@Data
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = false)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonIgnoreProperties(value = "transMap")
+public class IotProdInventoryAgingDO implements Serializable, TransPojo {
+
+    /**
+     * 财年
+     */
+    private String fyear;
+    /**
+     * 期间
+     */
+    private String fmonth;
+    /**
+     * 日期
+     */
+    private String fdate;
+    /**
+     * 工厂
+     */
+    private String werks;
+    /**
+     * 部门
+     */
+    private String dept;
+    /**
+     * 物料
+     */
+    private String matnr;
+    /**
+     * 库存地点
+     */
+    private String lgort;
+    /**
+     * 评估类
+     */
+    private String bklas;
+    /**
+     * 物料组
+     */
+    private String matkl;
+    /**
+     * 物料类型
+     */
+    private String mtart;
+    /**
+     * 物料描述
+     */
+    private String maktx;
+    /**
+     * 销售凭证
+     */
+    private String vbeln;
+    /**
+     * 销售项目
+     */
+    private String posnr;
+    /**
+     * 项目描述
+     */
+    private String bezei;
+    /**
+     * 总库存
+     */
+    private BigDecimal total;
+    /**
+     * 标准价格(元)
+     */
+    private BigDecimal stprs;
+    /**
+     * 行总库存成本(元)
+     */
+    private BigDecimal amttotal;
+    /**
+     * 价格单位
+     */
+    private BigDecimal peinh;
+    /**
+     * 库龄段1
+     */
+    private BigDecimal qty1;
+    /**
+     * 库龄段2
+     */
+    private BigDecimal qty2;
+    /**
+     * 库龄段3
+     */
+    private BigDecimal qty3;
+    /**
+     * 库龄段4
+     */
+    private BigDecimal qty4;
+    /**
+     * 库龄段1成本
+     */
+    private BigDecimal amt1;
+    /**
+     * 库龄段2成本
+     */
+    private BigDecimal amt2;
+    /**
+     * 库龄段3成本
+     */
+    private BigDecimal amt3;
+    /**
+     * 库龄段4成本
+     */
+    private BigDecimal amt4;
+    /**
+     * 库龄段其它成本
+     */
+    private BigDecimal amtn;
+
+}

+ 201 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotsapinventoryage/IotSapInventoryAgeDO.java

@@ -0,0 +1,201 @@
+package cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * PMS SAP 库龄 DO
+ *
+ * @author ruiqi
+ */
+@TableName("rq_iot_sap_inventory_age")
+@KeySequence("rq_iot_sap_inventory_age_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotSapInventoryAgeDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 组织部门id
+     */
+    private Long deptId;
+    /**
+     * sap工厂编号
+     */
+    private String factoryCode;
+    /**
+     * 工厂id (system_sap_org表id)
+     */
+    private Long factoryId;
+    /**
+     * 工厂(SAP) 专业公司
+     */
+    private String factory;
+    /**
+     * SAP库存地点编码
+     */
+    private String storageLocationCode;
+    /**
+     * 库存地点id
+     */
+    private Long storageLocationId;
+    /**
+     * 库存地点 - SAP 部门 项目部级 通用库 项目部库
+     */
+    private String projectDepartment;
+    /**
+     * 物料编码
+     */
+    private String materialCode;
+    /**
+     * 物料名称
+     */
+    private String materialName;
+    /**
+     * 物料组
+     */
+    private String materialGroupName;
+    /**
+     * 物料组id
+     */
+    private Long materialGroupId;
+    /**
+     * 数量
+     */
+    private BigDecimal quantity;
+    /**
+     * 单价
+     */
+    private BigDecimal unitPrice;
+    /**
+     * 总价
+     */
+    private BigDecimal totalPrice;
+    /**
+     * 评估类
+     */
+    private String valuationClass;
+    /**
+     * 币种
+     */
+    private String currency;
+    /**
+     * 财年
+     */
+    private String fiscalYear;
+    /**
+     * 期间
+     */
+    private String period;
+    /**
+     * 物料类型
+     */
+    private String materialType;
+    /**
+     * 物料类型描述
+     */
+    private String description;
+    /**
+     * 销售和分销凭证
+     */
+    private String salesDocument;
+    /**
+     * 项目
+     */
+    private String project;
+    /**
+     * 项目描述
+     */
+    private String comment;
+    /**
+     * 非限制
+     */
+    private BigDecimal unrestrictedUse;
+    /**
+     * 标准价格
+     */
+    private BigDecimal standardPrice;
+    /**
+     * 行总库存成本
+     */
+    private BigDecimal lineTotalInventoryCost;
+    /**
+     * 价格单位
+     */
+    private BigDecimal priceUnit;
+    /**
+     * 库龄段1
+     */
+    private BigDecimal agingBandOne;
+    /**
+     * 库龄段2
+     */
+    private BigDecimal agingBandTwo;
+    /**
+     * 库龄段3
+     */
+    private BigDecimal agingBandThree;
+    /**
+     * 库龄段4
+     */
+    private BigDecimal agingBandFour;
+    /**
+     * 库龄段1成本
+     */
+    private BigDecimal agingBandOneCost;
+    /**
+     * 库龄段2成本
+     */
+    private BigDecimal agingBandTwoCost;
+    /**
+     * 库龄段3成本
+     */
+    private BigDecimal agingBandThreeCost;
+    /**
+     * 库龄段4成本
+     */
+    private BigDecimal agingBandFourCost;
+    /**
+     * 库龄段其它成本
+     */
+    private BigDecimal agingBandOtherCost;
+    /**
+     * 同步状态 0未同步 1同步成功 2同步失败
+     */
+    private Integer syncStatus;
+    /**
+     * 最后一次同步时间
+     */
+    private LocalDateTime syncTime;
+    /**
+     * 同步失败时的错误信息
+     */
+    private String syncError;
+    /**
+     * 排序
+     */
+    private Integer sort;
+    /**
+     * 状态 0启用  1停用
+     */
+    private Integer status;
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 53 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprodinventoryaging/IotProdInventoryAgingMapper.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.pms.dal.mysql.iotprodinventoryaging;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 库存库龄表DWD Mapper
+ *
+ * @author ruiqi
+ */
+@Mapper
+public interface IotProdInventoryAgingMapper extends BaseMapperX<IotProdInventoryAgingDO> {
+
+    @DS("fanruan")
+    @TenantIgnore
+    default PageResult<IotProdInventoryAgingDO> selectPage(IotProdInventoryAgingPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<IotProdInventoryAgingDO>()
+                .eqIfPresent(IotProdInventoryAgingDO::getFyear, reqVO.getFyear())
+                .eqIfPresent(IotProdInventoryAgingDO::getFmonth, reqVO.getFmonth())
+                .eqIfPresent(IotProdInventoryAgingDO::getFdate, reqVO.getFdate())
+                .eqIfPresent(IotProdInventoryAgingDO::getWerks, reqVO.getWerks())
+                .eqIfPresent(IotProdInventoryAgingDO::getDept, reqVO.getDept())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatnr, reqVO.getMatnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getLgort, reqVO.getLgort())
+                .eqIfPresent(IotProdInventoryAgingDO::getBklas, reqVO.getBklas())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatkl, reqVO.getMatkl())
+                .eqIfPresent(IotProdInventoryAgingDO::getMtart, reqVO.getMtart())
+                .eqIfPresent(IotProdInventoryAgingDO::getMaktx, reqVO.getMaktx())
+                .eqIfPresent(IotProdInventoryAgingDO::getVbeln, reqVO.getVbeln())
+                .eqIfPresent(IotProdInventoryAgingDO::getPosnr, reqVO.getPosnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getBezei, reqVO.getBezei())
+                .eqIfPresent(IotProdInventoryAgingDO::getTotal, reqVO.getTotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getStprs, reqVO.getStprs())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmttotal, reqVO.getAmttotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getPeinh, reqVO.getPeinh())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty1, reqVO.getQty1())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty2, reqVO.getQty2())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty3, reqVO.getQty3())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty4, reqVO.getQty4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt1, reqVO.getAmt1())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt2, reqVO.getAmt2())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt3, reqVO.getAmt3())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt4, reqVO.getAmt4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmtn, reqVO.getAmtn()));
+    }
+
+}

+ 65 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotsapinventoryage/IotSapInventoryAgeMapper.java

@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.pms.dal.mysql.iotsapinventoryage;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgePageReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * PMS SAP 库龄 Mapper
+ *
+ * @author ruiqi
+ */
+@Mapper
+public interface IotSapInventoryAgeMapper extends BaseMapperX<IotSapInventoryAgeDO> {
+
+    default PageResult<IotSapInventoryAgeDO> selectPage(IotSapInventoryAgePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<IotSapInventoryAgeDO>()
+                .eqIfPresent(IotSapInventoryAgeDO::getDeptId, reqVO.getDeptId())
+                .eqIfPresent(IotSapInventoryAgeDO::getFactoryCode, reqVO.getFactoryCode())
+                .eqIfPresent(IotSapInventoryAgeDO::getFactoryId, reqVO.getFactoryId())
+                .eqIfPresent(IotSapInventoryAgeDO::getFactory, reqVO.getFactory())
+                .eqIfPresent(IotSapInventoryAgeDO::getStorageLocationCode, reqVO.getStorageLocationCode())
+                .eqIfPresent(IotSapInventoryAgeDO::getStorageLocationId, reqVO.getStorageLocationId())
+                .eqIfPresent(IotSapInventoryAgeDO::getProjectDepartment, reqVO.getProjectDepartment())
+                .eqIfPresent(IotSapInventoryAgeDO::getMaterialCode, reqVO.getMaterialCode())
+                .likeIfPresent(IotSapInventoryAgeDO::getMaterialName, reqVO.getMaterialName())
+                .likeIfPresent(IotSapInventoryAgeDO::getMaterialGroupName, reqVO.getMaterialGroupName())
+                .eqIfPresent(IotSapInventoryAgeDO::getMaterialGroupId, reqVO.getMaterialGroupId())
+                .eqIfPresent(IotSapInventoryAgeDO::getQuantity, reqVO.getQuantity())
+                .eqIfPresent(IotSapInventoryAgeDO::getUnitPrice, reqVO.getUnitPrice())
+                .eqIfPresent(IotSapInventoryAgeDO::getTotalPrice, reqVO.getTotalPrice())
+                .eqIfPresent(IotSapInventoryAgeDO::getValuationClass, reqVO.getValuationClass())
+                .eqIfPresent(IotSapInventoryAgeDO::getCurrency, reqVO.getCurrency())
+                .eqIfPresent(IotSapInventoryAgeDO::getFiscalYear, reqVO.getFiscalYear())
+                .eqIfPresent(IotSapInventoryAgeDO::getPeriod, reqVO.getPeriod())
+                .eqIfPresent(IotSapInventoryAgeDO::getMaterialType, reqVO.getMaterialType())
+                .eqIfPresent(IotSapInventoryAgeDO::getDescription, reqVO.getDescription())
+                .eqIfPresent(IotSapInventoryAgeDO::getSalesDocument, reqVO.getSalesDocument())
+                .eqIfPresent(IotSapInventoryAgeDO::getProject, reqVO.getProject())
+                .eqIfPresent(IotSapInventoryAgeDO::getComment, reqVO.getComment())
+                .eqIfPresent(IotSapInventoryAgeDO::getUnrestrictedUse, reqVO.getUnrestrictedUse())
+                .eqIfPresent(IotSapInventoryAgeDO::getStandardPrice, reqVO.getStandardPrice())
+                .eqIfPresent(IotSapInventoryAgeDO::getLineTotalInventoryCost, reqVO.getLineTotalInventoryCost())
+                .eqIfPresent(IotSapInventoryAgeDO::getPriceUnit, reqVO.getPriceUnit())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandOne, reqVO.getAgingBandOne())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandTwo, reqVO.getAgingBandTwo())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandThree, reqVO.getAgingBandThree())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandFour, reqVO.getAgingBandFour())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandOneCost, reqVO.getAgingBandOneCost())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandTwoCost, reqVO.getAgingBandTwoCost())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandThreeCost, reqVO.getAgingBandThreeCost())
+                .eqIfPresent(IotSapInventoryAgeDO::getAgingBandFourCost, reqVO.getAgingBandFourCost())
+                .eqIfPresent(IotSapInventoryAgeDO::getSyncStatus, reqVO.getSyncStatus())
+                .betweenIfPresent(IotSapInventoryAgeDO::getSyncTime, reqVO.getSyncTime())
+                .eqIfPresent(IotSapInventoryAgeDO::getSyncError, reqVO.getSyncError())
+                .eqIfPresent(IotSapInventoryAgeDO::getSort, reqVO.getSort())
+                .eqIfPresent(IotSapInventoryAgeDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(IotSapInventoryAgeDO::getRemark, reqVO.getRemark())
+                .betweenIfPresent(IotSapInventoryAgeDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(IotSapInventoryAgeDO::getId));
+    }
+
+}

+ 325 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/sap/SyncSapInventoryAgeJob.java

@@ -0,0 +1,325 @@
+package cn.iocoder.yudao.module.pms.job.sap;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotsapstock.IotSapStockMapper;
+import cn.iocoder.yudao.module.pms.sap.SapConnector;
+import cn.iocoder.yudao.module.pms.sap.service.IotSapService;
+import cn.iocoder.yudao.module.pms.sap.vo.IotSapInventoryAgeVO;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.saporg.SapOrgApi;
+import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
+import com.sap.conn.jco.JCoFunction;
+import com.sap.conn.jco.JCoParameterList;
+import com.sap.conn.jco.JCoTable;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
+
+/**
+ * 同步 SAP 库龄数据 定时任务
+ */
+@Component
+@Slf4j
+public class SyncSapInventoryAgeJob implements JobHandler {
+    @Autowired
+    private SapConnector sapConnector;
+    @Autowired
+    private SapOrgApi sapOrgApi;
+
+    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
+    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
+    @Autowired
+    private IotSapService iotSapService;
+    @Autowired
+    private IotSapStockMapper iotSapStockMapper;
+    @Autowired
+    private DeptApi deptApi;
+
+    @Override
+    @TenantIgnore
+    public String execute(String param) throws Exception {
+        // 查询所有工厂 根据工厂 多线程处理 SAP 库存
+        List<String> factoryCodes = factoryCodes();
+        if (CollUtil.isEmpty(factoryCodes)) {
+            return "No SAP Factory";
+        }
+        log.error("共找到 {} 个工厂需要同步: {}", factoryCodes.size(), factoryCodes);
+
+        // 提前获取并缓存基础数据,避免每个线程重复查询
+        SyncSapStockAgeContext syncContext = preloadSyncContext();
+        if (!syncContext.isValid()) {
+            return "基础数据加载失败,无法继续同步";
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        // 使用单线程循环处理每个工厂(移除了多线程)
+        List<FactorySyncResult> results = new ArrayList<>();
+        for (String factory : factoryCodes) {
+            try {
+                FactorySyncResult result = syncFactoryStockAgeData(factory, syncContext);
+                results.add(result);
+                log.info("工厂 {} 数据处理完成", factory);
+            } catch (Exception e) {
+                log.error("工厂 {} 处理过程中发生异常", factory, e);
+                results.add(FactorySyncResult.failure(factory, e));
+            }
+        }
+
+        // 汇总所有工厂的数据
+        List<IotSapInventoryAgeVO> allStockAgeData = new ArrayList<>();
+        List<String> failedFactories = new ArrayList<>();
+        List<String> successFactories = new ArrayList<>();
+
+        for (FactorySyncResult result : results) {
+            if (result.success) {
+                if (CollUtil.isNotEmpty(result.stockAgeData)) {
+                    allStockAgeData.addAll(result.stockAgeData);
+                }
+                successFactories.add(result.factoryCode);
+                log.error("工厂 {} SAP数据获取成功,获取 {} 条记录", result.factoryCode,
+                        CollUtil.size(result.stockAgeData));
+            } else {
+                failedFactories.add(result.factoryCode);
+                log.error("工厂 {} SAP数据获取失败", result.factoryCode, result.error);
+            }
+        }
+
+        // 统一处理所有库存数据
+        if (CollUtil.isNotEmpty(allStockAgeData)) {
+            try {
+                log.error("开始统一处理所有工厂的库龄数据,总计 {} 条记录", allStockAgeData.size());
+                iotSapService.processSapStockAgeBatch(allStockAgeData, syncContext);
+                log.error("库存数据处理完成");
+            } catch (Exception e) {
+                log.error("统一处理库龄数据时发生错误", e);
+                // 如果数据处理失败,认为所有工厂都失败
+                failedFactories.addAll(successFactories);
+                successFactories.clear();
+            }
+        }
+
+        long endTime = System.currentTimeMillis();
+        log.error("所有工厂同步完成,总耗时: {}ms", (endTime - startTime));
+
+        // 汇总执行结果
+        String result = buildResultMessage(factoryCodes.size(), successFactories.size(), failedFactories);
+        log.error("SAP库存同步任务完成: {}", result);
+
+        return result;
+    }
+
+    /**
+     * 同步单个工厂的库龄(仅获取数据,不处理)
+     */
+    private FactorySyncResult syncFactoryStockAgeData(String factory, SyncSapStockAgeContext context) {
+        JCoFunction function = null;
+        try {
+            // 每个线程使用独立的JCoFunction实例
+            function = context.destination.getRepository().getFunction("ZMM_GET_ZMM020");
+            if (function == null) {
+                throw new RuntimeException("未找到SAP函数 ZMM_GET_ZMM020");
+            }
+
+            // 设置输入参数
+            JCoParameterList input = function.getImportParameterList();
+            input.setValue("WERKS", factory);
+            input.setValue("LFMON", "20260901");
+            input.setValue("AGE01", 365);
+            input.setValue("AGE02", 365);
+            input.setValue("AGE03", 365);
+            input.setValue("AGE04", 365);
+            input.setValue("INC01", "X");
+            input.setValue("INC02", "X");
+            input.setValue("INC03", "X");
+
+            // 执行 RFC 调用
+            long startTime = System.currentTimeMillis();
+            function.execute(context.destination);
+            long endTime = System.currentTimeMillis();
+
+            log.error("工厂 {} SAP调用耗时: {}ms", factory, (endTime - startTime));
+
+            // 处理返回数据
+            JCoTable etStockTable = function.getTableParameterList().getTable("OUTTAB");
+            if (etStockTable == null) {
+                log.error("工厂 {} 未返回 OUTTAB 表数据", factory);
+                return FactorySyncResult.success(factory, Collections.emptyList());
+            }
+
+            int rowCount = etStockTable.getNumRows();
+            log.error("工厂 {} 获取到 {} 条库龄数据", factory, rowCount);
+
+            List<IotSapInventoryAgeVO> sapStockAges = Collections.emptyList();
+            if (rowCount > 0) {
+                sapStockAges = parseSapInventoryAgeData(etStockTable, factory);
+                log.error("工厂 {} 解析出 {} 条库龄记录", factory, sapStockAges.size());
+            }
+
+            return FactorySyncResult.success(factory, sapStockAges);
+
+        } catch (Exception e) {
+            log.error("工厂 {} SAP数据获取失败", factory, e);
+            return FactorySyncResult.failure(factory, e);
+        } finally {
+            // 清理JCo资源
+            if (function != null) {
+                try {
+                    // JCoFunction没有close方法,但可以显式清理
+                    function = null;
+                } catch (Exception e) {
+                    log.error("清理JCoFunction资源时发生警告", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 解析SAP库存数据
+     */
+    private List<IotSapInventoryAgeVO> parseSapInventoryAgeData(JCoTable etStockTable, String factory) {
+        List<IotSapInventoryAgeVO> sapStockAges = new ArrayList<>();
+
+        for (int i = 0; i < etStockTable.getNumRows(); i++) {
+            etStockTable.setRow(i);
+            try {
+                IotSapInventoryAgeVO sapStockAge = new IotSapInventoryAgeVO();
+                sapStockAge.setGJAHR(etStockTable.getString("GJAHR"));
+                sapStockAge.setRPMAX(etStockTable.getString("RPMAX"));
+                sapStockAge.setWERKS(etStockTable.getString("WERKS"));
+                sapStockAge.setMATNR(etStockTable.getString("MATNR"));
+                sapStockAge.setLGORT(etStockTable.getString("LGORT"));
+                sapStockAge.setBKLAS(etStockTable.getString("BKLAS"));
+                sapStockAge.setMATKL(etStockTable.getString("MATKL"));
+                sapStockAge.setMTART(etStockTable.getString("MTART"));
+                sapStockAge.setMAKTX(etStockTable.getString("MAKTX"));
+                sapStockAge.setVBELN(etStockTable.getString("VBELN"));
+                sapStockAge.setPOSNR(etStockTable.getString("POSNR"));
+                sapStockAge.setBEZEI(etStockTable.getString("BEZEI"));
+                sapStockAge.setTOTAL(etStockTable.getBigDecimal("TOTAL"));
+                sapStockAge.setSTPRS(etStockTable.getBigDecimal("STPRS"));
+
+                sapStockAge.setAMTTOTAL(etStockTable.getBigDecimal("AMTTOTAL"));
+                sapStockAge.setPEINH(etStockTable.getBigDecimal("PEINH"));
+                sapStockAge.setQTY1(etStockTable.getBigDecimal("QTY1"));
+                sapStockAge.setQTY2(etStockTable.getBigDecimal("QTY2"));
+                sapStockAge.setQTY3(etStockTable.getBigDecimal("QTY3"));
+                sapStockAge.setQTY4(etStockTable.getBigDecimal("QTY4"));
+                sapStockAge.setAMT1(etStockTable.getBigDecimal("AMT1"));
+                sapStockAge.setAMT2(etStockTable.getBigDecimal("AMT2"));
+                sapStockAge.setAMT3(etStockTable.getBigDecimal("AMT3"));
+                sapStockAge.setAMT4(etStockTable.getBigDecimal("AMT4"));
+                sapStockAge.setAMTN(etStockTable.getBigDecimal("AMTN"));
+                sapStockAges.add(sapStockAge);
+            } catch (Exception e) {
+                log.error("解析工厂 {} 第 {} 行库存数据时发生错误", factory, i, e);
+            }
+        }
+
+        return sapStockAges;
+    }
+
+    /**
+     * 构建结果消息
+     */
+    private String buildResultMessage(int total, int success, List<String> failedFactories) {
+        StringBuilder result = new StringBuilder();
+        result.append("SAP库龄同步完成。总计: ").append(total)
+                .append(", 成功: ").append(success)
+                .append(", 失败: ").append(total - success);
+
+        if (CollUtil.isNotEmpty(failedFactories)) {
+            result.append("。失败工厂: ").append(failedFactories);
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * 查询所有SAP工厂
+     */
+    private List<String> factoryCodes(){
+        List<SapOrgRespDTO> sapOrgs = sapOrgApi.getSapOrgByType(1);
+        if (CollUtil.isEmpty(sapOrgs)) {
+            return CollectionUtil.newArrayList();
+        }
+        List<String> factoryCodes = sapOrgs.stream()
+                .filter(dto -> "age".equals(dto.getCreator()))
+                .map(SapOrgRespDTO::getFactoryCode)
+                .collect(Collectors.toList());
+        return CollUtil.isEmpty(factoryCodes) ? CollUtil.empty(String.class) : factoryCodes;
+    }
+
+    /**
+     * 预加载同步上下文数据
+     */
+    private SyncSapStockAgeContext preloadSyncContext() {
+        SyncSapStockAgeContext context = new SyncSapStockAgeContext();
+        try {
+
+            // 提前获取SAP连接
+            context.destination = sapConnector.getDestination();
+            context.destination.ping(); // 测试连接
+
+            context.existStockAges = new ArrayList<>();
+            context.depts = deptApi.ignoredTenantDepts();
+            context.storageLocations = sapOrgApi.getSapOrgByType(3);
+            context.factories = sapOrgApi.getSapOrgByType(1);
+
+            // 构建映射关系
+            context.buildMappings();
+
+            log.error("同步上下文预加载完成: 工厂{}个, 库存地点{}个, 部门{}个, 现有库龄{}条",
+                    CollUtil.size(context.factories),
+                    CollUtil.size(context.storageLocations),
+                    CollUtil.size(context.depts),
+                    CollUtil.size(context.existStockAges));
+
+            System.out.println(String.format("工厂=%s, 库存地点=%s, 部门=%s, 现有库存=%s",
+                    CollUtil.size(context.factories), CollUtil.size(context.storageLocations), CollUtil.size(context.depts), CollUtil.size(context.existStockAges)));
+
+            return context;
+        } catch (Exception e) {
+            log.error("预加载同步上下文失败", e);
+            return context.invalid();
+        }
+    }
+
+    // 内部类用于封装工厂同步结果
+    private static class FactorySyncResult {
+        String factoryCode;
+        List<IotSapInventoryAgeVO> stockAgeData;
+        Exception error;
+        boolean success;
+
+        FactorySyncResult(String factoryCode) {
+            this.factoryCode = factoryCode;
+            this.success = false;
+        }
+
+        static FactorySyncResult success(String factoryCode, List<IotSapInventoryAgeVO> stockAgeData) {
+            FactorySyncResult result = new FactorySyncResult(factoryCode);
+            result.stockAgeData = stockAgeData;
+            result.success = true;
+            return result;
+        }
+
+        static FactorySyncResult failure(String factoryCode, Exception error) {
+            FactorySyncResult result = new FactorySyncResult(factoryCode);
+            result.error = error;
+            return result;
+        }
+    }
+}

+ 72 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/sap/SyncSapStockAgeContext.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.pms.job.sap;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
+import com.sap.conn.jco.JCoDestination;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class SyncSapStockAgeContext {
+    public JCoDestination destination;
+    public List<IotSapInventoryAgeDO> existStockAges;
+    public List<SapOrgRespDTO> storageLocations;
+    public List<SapOrgRespDTO> factories;
+    public List<DeptRespDTO> depts;
+
+    // 映射关系
+    public Map<String, Long> factoryIdPair = new HashMap<>();
+    public Map<String, String> factoryNamePair = new HashMap<>();
+    public Map<String, Long> storageLocationIdPair = new HashMap<>();
+    public Map<Long, String> stockLocationCodeIdPair = new HashMap<>();
+    public Map<String, Long> locationCodeDeptIdPair = new HashMap<>();
+    public Map<String, String> stockLocationNamePair = new HashMap<>();
+
+    boolean valid = false;
+
+    SyncSapStockAgeContext invalid() {
+        SyncSapStockAgeContext context = new SyncSapStockAgeContext();
+        valid = false;
+        return context;
+    }
+
+    boolean isValid() {
+        return valid && destination != null;
+    }
+
+    void buildMappings() {
+        // 工厂映射
+        factories.forEach(factory -> {
+            factoryIdPair.put(factory.getFactoryCode(), factory.getId());
+            factoryNamePair.put(factory.getFactoryCode(), factory.getFactoryName());
+        });
+
+        // 库存地点映射
+        storageLocations.forEach(location -> {
+            String uniqueKey = StrUtil.join("-", location.getFactoryCode(), location.getStorageLocationCode());
+            stockLocationCodeIdPair.put(location.getId(), uniqueKey);
+            storageLocationIdPair.put(uniqueKey, location.getId());
+            stockLocationNamePair.put(uniqueKey, location.getStorageLocationName());
+        });
+
+        // 部门映射
+        depts.forEach(dept -> {
+            Set<Long> stockLocationIds = dept.getStockLocationIds();
+            if (CollUtil.isNotEmpty(stockLocationIds)) {
+                stockLocationIds.forEach(locationId -> {
+                    if (stockLocationCodeIdPair.containsKey(locationId)) {
+                        String uniqueKey = stockLocationCodeIdPair.get(locationId);
+                        locationCodeDeptIdPair.put(uniqueKey, dept.getId());
+                    }
+                });
+            }
+        });
+
+        valid = true;
+    }
+}

+ 79 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/SapController.java

@@ -35,6 +35,85 @@ public class SapController {
     @Autowired
     private IotSapService iotSapService;
 
+    @GetMapping("/callSapBacklogInventory")
+    @Operation(summary = "同步sap 积压库存 账龄数据")
+    public String callSapBacklogInventory() {
+        try {
+            JCoDestination destination = sapConnector.getDestination();
+            JCoFunction function = destination.getRepository().getFunction("ZMM_GET_ZMM020");
+
+            // 设置输入参数
+            JCoParameterList input = function.getImportParameterList();
+            input.setValue("WERKS", "6000");
+            input.setValue("LFMON", "20260901");
+            input.setValue("AGE01", 365);
+            input.setValue("AGE02", 365);
+            input.setValue("AGE03", 365);
+            input.setValue("AGE04", 365);
+            input.setValue("INC01", "X");
+            input.setValue("INC02", "X");
+            input.setValue("INC03", "X");
+
+            // 执行 RFC 调用
+            function.execute(destination);
+
+            // 获取输出参数
+            JCoParameterList output = function.getTableParameterList();
+            JCoTable etStockTable = function.getTableParameterList().getTable("OUTTAB");
+            // JCoParameterList output = function.getExportParameterList();
+            if (CollUtil.isNotEmpty(output)) {
+                output.forEach(material -> {
+                    System.out.println(material.getName());
+                });
+            }
+
+            if (etStockTable != null && etStockTable.getNumRows() > 0) {
+                BigDecimal jiage = BigDecimal.ZERO;
+                // 库存地点集合
+                Set<String> storageLocations = new HashSet<>();
+                for (int i = 0; i < etStockTable.getNumRows(); i++) {
+                    etStockTable.setRow(i);
+                    System.out.println(String.format("行号 %d: 财年=%s, 期间=%s, 工厂=%s, 物料编号=%s, 库存地点=%s, 评估类=%s \n" +
+                                    "物料组=%s, 物料类型=%s, 物料描述=%s, 销售和分销凭证号=%s, 销售和分销凭证的项目号=%s, 描述=%s \n" +
+                            "非限制使用的估价的库存=%s, 标准价格=%s, 行总库存成本(元)=%s, 价格单位=%s, 库龄段1=%s, 库龄段2=%s \n" +
+                                    "库龄段3=%s, 库龄段4=%s, 库龄段1成本=%s, 库龄段2成本=%s, 库龄段3成本=%s, 库龄段4成本=%s, 库龄段其它成本=%s \n",
+                            i,
+                            etStockTable.getString("GJAHR"),    // 财年
+                            etStockTable.getString("RPMAX"),    // 期间
+                            etStockTable.getString("WERKS"),    // 工厂
+                            etStockTable.getString("MATNR"),    // 物料编号
+                            etStockTable.getString("LGORT"),    // 库存地点
+                            etStockTable.getString("BKLAS"),    // 评估类
+                            etStockTable.getString("MATKL"),    // 物料组
+                            etStockTable.getString("MTART"),    // 物料类型
+                            etStockTable.getString("MAKTX"),    // 物料描述
+                            etStockTable.getString("VBELN"),    // 销售和分销凭证号
+                            etStockTable.getString("POSNR"),    // 销售和分销凭证的项目号
+                            etStockTable.getString("BEZEI"),    // 描述
+                            etStockTable.getBigDecimal("TOTAL"),     // 非限制使用的估价的库存
+                            etStockTable.getBigDecimal("STPRS"),     // 标准价格
+                            etStockTable.getBigDecimal("AMTTOTAL"),     // 行总库存成本(元)
+                            etStockTable.getBigDecimal("PEINH"),    // 价格单位
+                            etStockTable.getBigDecimal("QTY1"),     // 库龄段1
+                            etStockTable.getBigDecimal("QTY2"),     // 库龄段2
+
+                            etStockTable.getBigDecimal("QTY3"),     // 库龄段3
+                            etStockTable.getBigDecimal("QTY4"),     // 库龄段4
+                            etStockTable.getBigDecimal("AMT1"),     // 库龄段1成本
+                            etStockTable.getBigDecimal("AMT2"),    // 库龄段2成本
+                            etStockTable.getBigDecimal("AMT3"),     // 库龄段3成本
+                            etStockTable.getBigDecimal("AMT4"),     // 库龄段4成本
+                            etStockTable.getBigDecimal("AMTN")     // 库龄段其它成本
+                    ));
+
+                }
+            }
+            return "Material Info: " + output.getString("OUTTAB");
+        } catch (JCoException e) {
+            return "Error calling SAP Stock: " + e.getMessage();
+        }
+    }
+
     @GetMapping("/callSapStock")
     @Operation(summary = "同步sap 库存接口")
     public String callSapStock() {

+ 7 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/service/IotSapService.java

@@ -47,4 +47,11 @@ public interface IotSapService {
      * @param context 同步上下文,包含基础数据映射
      */
     void processSapStockBatch(List<IotSapStockVO> allStockData, Object context);
+
+    /**
+     * 批量处理SAP 库龄数据
+     * @param allStockAges 所有工厂的库龄数据
+     * @param context 同步上下文,包含基础数据映射
+     */
+    void processSapStockAgeBatch(List<IotSapInventoryAgeVO> allStockAges, Object context);
 }

+ 155 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/service/IotSapServiceImpl.java

@@ -10,15 +10,18 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotsappickinglist.vo.IotSapP
 import cn.iocoder.yudao.module.pms.controller.admin.iotsapstock.vo.IotSapStockPageReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotlockstock.IotLockStockDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmaterial.IotMaterialDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotsappickinglist.IotSapPickingListDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapstock.IotSapStockDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapstocklog.IotSapStockLogDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotlockstock.IotLockStockMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmaterial.IotMaterialMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmaterial.IotSapMaterialMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotsapinventoryage.IotSapInventoryAgeMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotsappickinglist.IotSapPickingListMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotsapstock.IotSapStockMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotsapstocklog.IotSapStockLogMapper;
+import cn.iocoder.yudao.module.pms.job.sap.SyncSapStockAgeContext;
 import cn.iocoder.yudao.module.pms.job.sap.SyncSapStockContext;
 import cn.iocoder.yudao.module.pms.sap.vo.*;
 import cn.iocoder.yudao.module.supplier.dal.dataobject.product.SupplierDO;
@@ -27,6 +30,7 @@ import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.saporg.SapOrgApi;
 import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
+import com.google.common.collect.ImmutableMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -69,6 +73,8 @@ public class IotSapServiceImpl implements IotSapService {
     @Autowired
     private IotSapStockMapper iotSapStockMapper;
     @Autowired
+    private IotSapInventoryAgeMapper iotSapInventoryAgeMapper;
+    @Autowired
     private IotSapStockLogMapper iotSapStockLogMapper;
     @Autowired
     private IotSapPickingListMapper iotSapPickingListMapper;
@@ -787,6 +793,56 @@ public class IotSapServiceImpl implements IotSapService {
         }
     }
 
+    @Override
+    public void processSapStockAgeBatch(List<IotSapInventoryAgeVO> allStockAges, Object contextObj) {
+        if (!(contextObj instanceof SyncSapStockAgeContext)) {
+            throw new IllegalArgumentException("无效的上下文对象");
+        }
+
+        SyncSapStockAgeContext context = (SyncSapStockAgeContext) contextObj;
+
+        if (CollUtil.isEmpty(allStockAges)) {
+            log.error("没有需要处理的SAP库龄数据");
+            return;
+        }
+
+        log.error("开始批量处理SAP库龄数据,总计 {} 条记录", allStockAges.size());
+        long startTime = System.currentTimeMillis();
+
+        try {
+            // 按工厂分组处理
+            Map<String, List<IotSapInventoryAgeVO>> factoryStocksAgeMap = allStockAges.stream()
+                    .collect(Collectors.groupingBy(IotSapInventoryAgeVO::getWERKS));
+
+            log.error("按工厂分组完成,共 {} 个工厂的库龄数据需要处理", factoryStocksAgeMap.size());
+
+            List<IotSapInventoryAgeDO> totalActualUpdatedStocks = new ArrayList<>();
+            List<IotSapInventoryAgeDO> totalTobeAddedStocks = new ArrayList<>();
+
+            // 处理每个工厂的数据
+            factoryStocksAgeMap.forEach((factoryCode, stocks) -> {
+                try {
+                    processSingleFactoryStockAges(factoryCode, stocks, context, totalTobeAddedStocks, totalActualUpdatedStocks);
+                    log.error("工厂 {} 库龄数据处理完成,共处理 {} 条记录", factoryCode, stocks.size());
+                } catch (Exception e) {
+                    log.error("处理工厂 {} 库存数据时发生错误", factoryCode, e);
+                    e.printStackTrace();
+                    // 记录错误但继续处理其他工厂
+                }
+            });
+
+            // 分批处理新增库存数据 先删除再新增
+            processNewStockAgesInBatches(totalTobeAddedStocks);
+
+            long endTime = System.currentTimeMillis();
+            log.error("所有工厂库龄数据处理完成,总耗时: {}ms", (endTime - startTime));
+
+        } catch (Exception e) {
+            log.error("批量处理SAP库龄数据时发生错误", e);
+            throw new RuntimeException("SAP库龄数据处理失败", e);
+        }
+    }
+
 
     /**
      * 分批处理库存日志数据
@@ -932,6 +988,53 @@ public class IotSapServiceImpl implements IotSapService {
         }
     }
 
+    /**
+     * 分批处理新增库龄数据
+     */
+    private void processNewStockAgesInBatches(List<IotSapInventoryAgeDO> newStockAges) {
+        if (CollUtil.isEmpty(newStockAges)) {
+            log.error("没有需要新增的库龄数据");
+            return;
+        }
+
+        log.error("开始分批插入新增库龄数据,总计 {} 条记录", newStockAges.size());
+
+        List<List<IotSapInventoryAgeDO>> batches = CollUtil.split(newStockAges, BATCH_SIZE);
+        CountDownLatch latch = new CountDownLatch(batches.size());
+        final int[] successCount = {0};
+
+        for (List<IotSapInventoryAgeDO> batch : batches) {
+            pmsThreadPoolTaskExecutor.execute(() -> {
+                try {
+                    TenantUtils.execute(1L, () -> {
+                        try {
+                            iotSapInventoryAgeMapper.insertBatch(batch);
+                            successCount[0] += batch.size();
+                            log.error("成功插入一批新增库龄,数量: {}", batch.size());
+                        } catch (Exception e) {
+                            log.error("插入新增库龄批次时发生错误,批次大小: {}", batch.size(), e);
+                            e.printStackTrace();
+                        }
+                        return null;
+                    });
+                } catch (Exception e) {
+                    log.error("执行新增库龄租户上下文操作时发生错误", e);
+                    e.printStackTrace();
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+
+        try {
+            latch.await();
+            log.error("新增库龄数据插入完成,成功插入 {} 条记录", successCount[0]);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.error("等待新增库龄插入完成时被中断", e);
+        }
+    }
+
     /**
      * 分批处理更新库存数据
      */
@@ -1254,4 +1357,56 @@ public class IotSapServiceImpl implements IotSapService {
                 CollUtil.size(tobeAddedStocks),
                 CollUtil.size(actualUpdatedStocks));
     }
+
+    private void processSingleFactoryStockAges(String factoryCode, List<IotSapInventoryAgeVO> stockAges, SyncSapStockAgeContext context,
+                                               List<IotSapInventoryAgeDO> totalTobeAddedStockAges, List<IotSapInventoryAgeDO> totalActualUpdatedStockAges) {
+        if (CollUtil.isEmpty(stockAges)) {
+            log.error("工厂 {} 没有库龄数据需要处理", factoryCode);
+            return;
+        }
+        // 先删除当前工厂下的所有库龄数据,再新增
+        int count = iotSapInventoryAgeMapper.deleteByMap(ImmutableMap.of(
+                "factory_code", factoryCode
+        ));
+
+        // 准备新增的库存数据
+        List<IotSapInventoryAgeDO> tobeAddedStockAges = new ArrayList<>();
+        stockAges.forEach(stockAge -> {
+            IotSapInventoryAgeDO sapStockAge = new IotSapInventoryAgeDO();
+
+            sapStockAge.setFiscalYear(stockAge.getGJAHR());
+            sapStockAge.setPeriod(stockAge.getRPMAX());
+            sapStockAge.setFactoryCode(stockAge.getWERKS());
+            sapStockAge.setMaterialCode(stockAge.getMATNR());
+            sapStockAge.setStorageLocationCode(stockAge.getLGORT());
+            sapStockAge.setValuationClass(stockAge.getBKLAS());
+            sapStockAge.setMaterialGroupName(stockAge.getMATKL());
+            sapStockAge.setMaterialType(stockAge.getMTART());
+            sapStockAge.setDescription(stockAge.getMAKTX());
+            sapStockAge.setSalesDocument(stockAge.getVBELN());
+            sapStockAge.setProject(stockAge.getPOSNR());
+            sapStockAge.setComment(stockAge.getBEZEI());
+            sapStockAge.setUnrestrictedUse(stockAge.getTOTAL());
+            sapStockAge.setStandardPrice(stockAge.getSTPRS());
+            sapStockAge.setLineTotalInventoryCost(stockAge.getAMTTOTAL());
+            sapStockAge.setPriceUnit(stockAge.getPEINH());
+
+            sapStockAge.setAgingBandOne(stockAge.getQTY1());
+            sapStockAge.setAgingBandTwo(stockAge.getQTY2());
+            sapStockAge.setAgingBandThree(stockAge.getQTY3());
+            sapStockAge.setAgingBandFour(stockAge.getQTY4());
+            sapStockAge.setAgingBandOneCost(stockAge.getAMT1());
+            sapStockAge.setAgingBandTwoCost(stockAge.getAMT2());
+            sapStockAge.setAgingBandThreeCost(stockAge.getAMT3());
+            sapStockAge.setAgingBandFourCost(stockAge.getAMT4());
+            sapStockAge.setAgingBandOtherCost(stockAge.getAMTN());
+
+            tobeAddedStockAges.add(sapStockAge);
+        });
+
+        // 本地库存 初始化 批量插入SAP 库龄 工厂 库存地点 已经关联了组织架构部门
+        if (CollUtil.isNotEmpty(tobeAddedStockAges)) {
+            totalTobeAddedStockAges.addAll(tobeAddedStockAges);
+        }
+    }
 }

+ 89 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/sap/vo/IotSapInventoryAgeVO.java

@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.pms.sap.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - SAP 库龄分析 VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotSapInventoryAgeVO {
+
+    @Schema(description = "财年", example = "")
+    private String GJAHR;
+
+    @Schema(description = "期间", example = "")
+    private String RPMAX;
+
+    @Schema(description = "工厂", example = "")
+    private String WERKS;
+
+    @Schema(description = "物料", example = "")
+    private String MATNR;
+
+    @Schema(description = "库存地点", example = "")
+    private String LGORT;
+
+    @Schema(description = "评估类", example = "")
+    private String BKLAS;
+
+    @Schema(description = "物料组", example = "")
+    private String MATKL;
+
+    @Schema(description = "物料类型", example = "")
+    private String MTART;
+
+    @Schema(description = "物料类型描述", example = "")
+    private String MAKTX;
+
+    @Schema(description = "销售和分销凭证", example = "")
+    private String VBELN;
+
+    @Schema(description = "项目", example = "")
+    private String POSNR;
+
+    @Schema(description = "项目描述", example = "")
+    private String BEZEI;
+
+    @Schema(description = "非限制", example = "21.3")
+    private BigDecimal TOTAL;
+
+    @Schema(description = "标准价格", example = "21.3")
+    private BigDecimal STPRS;
+
+    @Schema(description = "行总库存成本", example = "21.3")
+    private BigDecimal AMTTOTAL;
+
+    @Schema(description = "价格单位", example = "21.3")
+    private BigDecimal PEINH;
+
+    @Schema(description = "库龄段1", example = "21.3")
+    private BigDecimal QTY1;
+
+    @Schema(description = "库龄段2", example = "21.3")
+    private BigDecimal QTY2;
+
+    @Schema(description = "库龄段3", example = "21.3")
+    private BigDecimal QTY3;
+
+    @Schema(description = "库龄段4", example = "21.3")
+    private BigDecimal QTY4;
+
+    @Schema(description = "库龄段1成本", example = "21.3")
+    private BigDecimal AMT1;
+
+    @Schema(description = "库龄段2成本", example = "21.3")
+    private BigDecimal AMT2;
+
+    @Schema(description = "库龄段3成本", example = "21.3")
+    private BigDecimal AMT3;
+
+    @Schema(description = "库龄段4成本", example = "21.3")
+    private BigDecimal AMT4;
+
+    @Schema(description = "库龄段其它成本", example = "21.3")
+    private BigDecimal AMTN;
+
+}

+ 55 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingService.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
+
+import javax.validation.Valid;
+
+/**
+ * 库存库龄表DWD Service 接口
+ *
+ * @author ruiqi
+ */
+public interface IotProdInventoryAgingService {
+
+    /**
+     * 创建库存库龄表DWD
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createIotProdInventoryAging(@Valid IotProdInventoryAgingSaveReqVO createReqVO);
+
+    /**
+     * 更新库存库龄表DWD
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateIotProdInventoryAging(@Valid IotProdInventoryAgingSaveReqVO updateReqVO);
+
+    /**
+     * 删除库存库龄表DWD
+     *
+     * @param id 编号
+     */
+    void deleteIotProdInventoryAging(Long id);
+
+    /**
+     * 获得库存库龄表DWD
+     *
+     * @param id 编号
+     * @return 库存库龄表DWD
+     */
+    IotProdInventoryAgingDO getIotProdInventoryAging(Long id);
+
+    /**
+     * 获得库存库龄表DWD分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 库存库龄表DWD分页
+     */
+    PageResult<IotProdInventoryAgingDO> getIotProdInventoryAgingPage(IotProdInventoryAgingPageReqVO pageReqVO);
+
+}

+ 71 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingServiceImpl.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotprodinventoryaging.IotProdInventoryAgingMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROD_INVENTORY_AGING_NOT_EXISTS;
+
+/**
+ * 库存库龄表DWD Service 实现类
+ *
+ * @author ruiqi
+ */
+@Service
+@Validated
+public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingService {
+
+    @Resource
+    private IotProdInventoryAgingMapper iotProdInventoryAgingMapper;
+
+    @Override
+    public Long createIotProdInventoryAging(IotProdInventoryAgingSaveReqVO createReqVO) {
+        // 插入
+        IotProdInventoryAgingDO iotProdInventoryAging = BeanUtils.toBean(createReqVO, IotProdInventoryAgingDO.class);
+        iotProdInventoryAgingMapper.insert(iotProdInventoryAging);
+        // 返回
+        return null;
+    }
+
+    @Override
+    public void updateIotProdInventoryAging(IotProdInventoryAgingSaveReqVO updateReqVO) {
+        // 校验存在
+        validateIotProdInventoryAgingExists(updateReqVO.getId());
+        // 更新
+        IotProdInventoryAgingDO updateObj = BeanUtils.toBean(updateReqVO, IotProdInventoryAgingDO.class);
+        iotProdInventoryAgingMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteIotProdInventoryAging(Long id) {
+        // 校验存在
+        validateIotProdInventoryAgingExists(id);
+        // 删除
+        iotProdInventoryAgingMapper.deleteById(id);
+    }
+
+    private void validateIotProdInventoryAgingExists(Long id) {
+        if (iotProdInventoryAgingMapper.selectById(id) == null) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public IotProdInventoryAgingDO getIotProdInventoryAging(Long id) {
+        return iotProdInventoryAgingMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<IotProdInventoryAgingDO> getIotProdInventoryAgingPage(IotProdInventoryAgingPageReqVO pageReqVO) {
+        return iotProdInventoryAgingMapper.selectPage(pageReqVO);
+    }
+
+}

+ 55 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotsapinventoryage/IotSapInventoryAgeService.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.pms.service.iotsapinventoryage;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgePageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgeSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
+
+import javax.validation.Valid;
+
+/**
+ * PMS SAP 库龄 Service 接口
+ *
+ * @author ruiqi
+ */
+public interface IotSapInventoryAgeService {
+
+    /**
+     * 创建PMS SAP 库龄
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createIotSapInventoryAge(@Valid IotSapInventoryAgeSaveReqVO createReqVO);
+
+    /**
+     * 更新PMS SAP 库龄
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateIotSapInventoryAge(@Valid IotSapInventoryAgeSaveReqVO updateReqVO);
+
+    /**
+     * 删除PMS SAP 库龄
+     *
+     * @param id 编号
+     */
+    void deleteIotSapInventoryAge(Long id);
+
+    /**
+     * 获得PMS SAP 库龄
+     *
+     * @param id 编号
+     * @return PMS SAP 库龄
+     */
+    IotSapInventoryAgeDO getIotSapInventoryAge(Long id);
+
+    /**
+     * 获得PMS SAP 库龄分页
+     *
+     * @param pageReqVO 分页查询
+     * @return PMS SAP 库龄分页
+     */
+    PageResult<IotSapInventoryAgeDO> getIotSapInventoryAgePage(IotSapInventoryAgePageReqVO pageReqVO);
+
+}

+ 72 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotsapinventoryage/IotSapInventoryAgeServiceImpl.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.pms.service.iotsapinventoryage;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgePageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotsapinventoryage.vo.IotSapInventoryAgeSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapinventoryage.IotSapInventoryAgeDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotsapinventoryage.IotSapInventoryAgeMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_SAP_INVENTORY_AGE_NOT_EXISTS;
+
+
+/**
+ * PMS SAP 库龄 Service 实现类
+ *
+ * @author ruiqi
+ */
+@Service
+@Validated
+public class IotSapInventoryAgeServiceImpl implements IotSapInventoryAgeService {
+
+    @Resource
+    private IotSapInventoryAgeMapper iotSapInventoryAgeMapper;
+
+    @Override
+    public Long createIotSapInventoryAge(IotSapInventoryAgeSaveReqVO createReqVO) {
+        // 插入
+        IotSapInventoryAgeDO iotSapInventoryAge = BeanUtils.toBean(createReqVO, IotSapInventoryAgeDO.class);
+        iotSapInventoryAgeMapper.insert(iotSapInventoryAge);
+        // 返回
+        return iotSapInventoryAge.getId();
+    }
+
+    @Override
+    public void updateIotSapInventoryAge(IotSapInventoryAgeSaveReqVO updateReqVO) {
+        // 校验存在
+        validateIotSapInventoryAgeExists(updateReqVO.getId());
+        // 更新
+        IotSapInventoryAgeDO updateObj = BeanUtils.toBean(updateReqVO, IotSapInventoryAgeDO.class);
+        iotSapInventoryAgeMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteIotSapInventoryAge(Long id) {
+        // 校验存在
+        validateIotSapInventoryAgeExists(id);
+        // 删除
+        iotSapInventoryAgeMapper.deleteById(id);
+    }
+
+    private void validateIotSapInventoryAgeExists(Long id) {
+        if (iotSapInventoryAgeMapper.selectById(id) == null) {
+            throw exception(IOT_SAP_INVENTORY_AGE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public IotSapInventoryAgeDO getIotSapInventoryAge(Long id) {
+        return iotSapInventoryAgeMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<IotSapInventoryAgeDO> getIotSapInventoryAgePage(IotSapInventoryAgePageReqVO pageReqVO) {
+        return iotSapInventoryAgeMapper.selectPage(pageReqVO);
+    }
+
+}

+ 5 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/saporg/dto/SapOrgRespDTO.java

@@ -67,6 +67,11 @@ public class SapOrgRespDTO {
      */
     private Integer status;
 
+    /**
+     * 创建人 扩展字段 库龄分析 age
+     */
+    private String creator;
+
     /**
      * 部门编号
      */