diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/controller/AmsInboundDetailReportController.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/controller/AmsInboundDetailReportController.java new file mode 100644 index 0000000..1f8bc31 --- /dev/null +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/controller/AmsInboundDetailReportController.java @@ -0,0 +1,103 @@ +package com.ruoyi.asset.controller; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import com.ruoyi.asset.domain.AmsAssetCategory; +import com.ruoyi.asset.domain.AmsInboundDetailReport; +import com.ruoyi.asset.domain.AmsWarehouse; +import com.ruoyi.asset.service.IAmsAssetCategoryService; +import com.ruoyi.asset.service.IAmsInboundDetailReportService; +import com.ruoyi.asset.service.IAmsWarehouseService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; + +/** + * 入库明细报表Controller。 + * + * @author Yangk + */ +@Controller +@RequestMapping("/asset/report/inbound") +public class AmsInboundDetailReportController extends BaseController +{ + private static final String ENABLED_YES = "Y"; + + private String prefix = "asset/report"; + + @Autowired + private IAmsInboundDetailReportService amsInboundDetailReportService; + + @Autowired + private IAmsAssetCategoryService amsAssetCategoryService; + + @Autowired + private IAmsWarehouseService amsWarehouseService; + + @RequiresPermissions("asset:report:inbound:view") + @GetMapping() + public String inbound(ModelMap mmap) + { + putReportOptions(mmap); + return prefix + "/inbound"; + } + + /** + * 查询入库明细报表列表。 + */ + @RequiresPermissions("asset:report:inbound:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(AmsInboundDetailReport amsInboundDetailReport) + { + startPage(); + List list = amsInboundDetailReportService + .selectInboundDetailReportList(amsInboundDetailReport); + return getDataTable(list); + } + + /** + * 导出入库明细报表。 + */ + @RequiresPermissions("asset:report:inbound:export") + @Log(title = "入库明细报表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(AmsInboundDetailReport amsInboundDetailReport) + { + List list = amsInboundDetailReportService + .selectInboundDetailReportList(amsInboundDetailReport); + ExcelUtil util = new ExcelUtil(AmsInboundDetailReport.class); + return util.exportExcel(list, "入库明细报表数据"); + } + + private void putReportOptions(ModelMap mmap) + { + mmap.put("warehouseList", selectEnabledWarehouseList()); + mmap.put("categoryList", selectEnabledCategoryList()); + } + + private List selectEnabledWarehouseList() + { + AmsWarehouse warehouse = new AmsWarehouse(); + warehouse.setEnabled(ENABLED_YES); + return amsWarehouseService.selectAmsWarehouseList(warehouse); + } + + private List selectEnabledCategoryList() + { + AmsAssetCategory category = new AmsAssetCategory(); + category.setEnabled(ENABLED_YES); + return amsAssetCategoryService.selectAmsAssetCategoryList(category); + } +} diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/domain/AmsInboundDetailReport.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/domain/AmsInboundDetailReport.java new file mode 100644 index 0000000..e23c196 --- /dev/null +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/domain/AmsInboundDetailReport.java @@ -0,0 +1,234 @@ +package com.ruoyi.asset.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 入库明细报表对象。 + * + * @author Yangk + */ +public class AmsInboundDetailReport extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 入库单号 */ + @Excel(name = "入库单号") + private String inboundNo; + + /** 入库时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Excel(name = "入库时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date inboundTime; + + /** 入库仓库ID */ + private Long warehouseId; + + /** 入库仓库 */ + @Excel(name = "入库仓库") + private String warehouseName; + + /** 入库人 */ + @Excel(name = "入库人") + private String inboundUserName; + + /** 资产编码 */ + @Excel(name = "资产编码") + private String assetCode; + + /** 资产名称 */ + @Excel(name = "资产名称") + private String assetName; + + /** 资产类别ID */ + private Long categoryId; + + /** 资产类别 */ + @Excel(name = "资产类别") + private String categoryName; + + /** 规格型号 */ + @Excel(name = "规格型号") + private String specModel; + + /** 品牌 */ + @Excel(name = "品牌") + private String brand; + + /** 入库位置ID */ + private Long locationId; + + /** 入库位置 */ + @Excel(name = "入库位置") + private String locationName; + + /** 入库数量 */ + @Excel(name = "入库数量", cellType = ColumnType.NUMERIC) + private Integer inboundQuantity; + + public String getInboundNo() + { + return inboundNo; + } + + public void setInboundNo(String inboundNo) + { + this.inboundNo = inboundNo; + } + + public Date getInboundTime() + { + return inboundTime; + } + + public void setInboundTime(Date inboundTime) + { + this.inboundTime = inboundTime; + } + + public Long getWarehouseId() + { + return warehouseId; + } + + public void setWarehouseId(Long warehouseId) + { + this.warehouseId = warehouseId; + } + + public String getWarehouseName() + { + return warehouseName; + } + + public void setWarehouseName(String warehouseName) + { + this.warehouseName = warehouseName; + } + + public String getInboundUserName() + { + return inboundUserName; + } + + public void setInboundUserName(String inboundUserName) + { + this.inboundUserName = inboundUserName; + } + + public String getAssetCode() + { + return assetCode; + } + + public void setAssetCode(String assetCode) + { + this.assetCode = assetCode; + } + + public String getAssetName() + { + return assetName; + } + + public void setAssetName(String assetName) + { + this.assetName = assetName; + } + + public Long getCategoryId() + { + return categoryId; + } + + public void setCategoryId(Long categoryId) + { + this.categoryId = categoryId; + } + + public String getCategoryName() + { + return categoryName; + } + + public void setCategoryName(String categoryName) + { + this.categoryName = categoryName; + } + + public String getSpecModel() + { + return specModel; + } + + public void setSpecModel(String specModel) + { + this.specModel = specModel; + } + + public String getBrand() + { + return brand; + } + + public void setBrand(String brand) + { + this.brand = brand; + } + + public Long getLocationId() + { + return locationId; + } + + public void setLocationId(Long locationId) + { + this.locationId = locationId; + } + + public String getLocationName() + { + return locationName; + } + + public void setLocationName(String locationName) + { + this.locationName = locationName; + } + + public Integer getInboundQuantity() + { + return inboundQuantity; + } + + public void setInboundQuantity(Integer inboundQuantity) + { + this.inboundQuantity = inboundQuantity; + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("inboundNo", getInboundNo()) + .append("inboundTime", getInboundTime()) + .append("warehouseId", getWarehouseId()) + .append("warehouseName", getWarehouseName()) + .append("inboundUserName", getInboundUserName()) + .append("assetCode", getAssetCode()) + .append("assetName", getAssetName()) + .append("categoryId", getCategoryId()) + .append("categoryName", getCategoryName()) + .append("specModel", getSpecModel()) + .append("brand", getBrand()) + .append("locationId", getLocationId()) + .append("locationName", getLocationName()) + .append("inboundQuantity", getInboundQuantity()) + .toString(); + } +} diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/mapper/AmsInboundDetailReportMapper.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/mapper/AmsInboundDetailReportMapper.java new file mode 100644 index 0000000..79c6207 --- /dev/null +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/mapper/AmsInboundDetailReportMapper.java @@ -0,0 +1,23 @@ +package com.ruoyi.asset.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.asset.domain.AmsInboundDetailReport; + +/** + * 入库明细报表Mapper接口。 + * + * @author Yangk + */ +public interface AmsInboundDetailReportMapper +{ + /** + * 查询入库明细报表列表。 + * + * @param report 查询条件 + * @param doneStatus 入库完成状态 + * @return 入库明细报表集合 + */ + public List selectInboundDetailReportList( + @Param("report") AmsInboundDetailReport report, @Param("doneStatus") String doneStatus); +} diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/service/IAmsInboundDetailReportService.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/IAmsInboundDetailReportService.java new file mode 100644 index 0000000..07283f9 --- /dev/null +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/IAmsInboundDetailReportService.java @@ -0,0 +1,20 @@ +package com.ruoyi.asset.service; + +import java.util.List; +import com.ruoyi.asset.domain.AmsInboundDetailReport; + +/** + * 入库明细报表Service接口。 + * + * @author Yangk + */ +public interface IAmsInboundDetailReportService +{ + /** + * 查询入库明细报表列表。 + * + * @param amsInboundDetailReport 查询条件 + * @return 入库明细报表集合 + */ + public List selectInboundDetailReportList(AmsInboundDetailReport amsInboundDetailReport); +} diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImpl.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImpl.java new file mode 100644 index 0000000..f6fca9b --- /dev/null +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImpl.java @@ -0,0 +1,33 @@ +package com.ruoyi.asset.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.asset.constant.InboundOrderStatus; +import com.ruoyi.asset.domain.AmsInboundDetailReport; +import com.ruoyi.asset.mapper.AmsInboundDetailReportMapper; +import com.ruoyi.asset.service.IAmsInboundDetailReportService; + +/** + * 入库明细报表Service业务层处理。 + * + * @author Yangk + */ +@Service +public class AmsInboundDetailReportServiceImpl implements IAmsInboundDetailReportService +{ + @Autowired + private AmsInboundDetailReportMapper amsInboundDetailReportMapper; + + /** + * 查询入库明细报表列表。 + */ + @Override + public List selectInboundDetailReportList(AmsInboundDetailReport amsInboundDetailReport) + { + AmsInboundDetailReport query = amsInboundDetailReport == null ? new AmsInboundDetailReport() + : amsInboundDetailReport; + // 报表只统计完成态数据,状态由服务端固定,避免前端参数把草稿单据混入报表。 + return amsInboundDetailReportMapper.selectInboundDetailReportList(query, InboundOrderStatus.INBOUND_DONE); + } +} diff --git a/ruoyi-asset/src/main/resources/mapper/asset/AmsInboundDetailReportMapper.xml b/ruoyi-asset/src/main/resources/mapper/asset/AmsInboundDetailReportMapper.xml new file mode 100644 index 0000000..4cbb860 --- /dev/null +++ b/ruoyi-asset/src/main/resources/mapper/asset/AmsInboundDetailReportMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + o.del_flag = '0' + and i.del_flag = '0' + and o.order_status = #{doneStatus} + + and o.inbound_no like concat(#{report.inboundNo}, '%') + + + and o.warehouse_id = #{report.warehouseId} + + + and o.inbound_user_name like concat('%', #{report.inboundUserName}, '%') + + + and i.category_id = #{report.categoryId} + + + and i.asset_code like concat(#{report.assetCode}, '%') + + + and o.inbound_time >= #{report.params.beginInboundTime} + + + and o.inbound_time < date_add(#{report.params.endInboundTime}, interval 1 day) + + + + + + diff --git a/ruoyi-asset/src/main/resources/templates/asset/report/inbound.html b/ruoyi-asset/src/main/resources/templates/asset/report/inbound.html new file mode 100644 index 0000000..89d45ec --- /dev/null +++ b/ruoyi-asset/src/main/resources/templates/asset/report/inbound.html @@ -0,0 +1,147 @@ + + + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + + - + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + +  搜索 + + +  重置 + +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + + + diff --git a/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImplTest.java b/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImplTest.java new file mode 100644 index 0000000..fc68595 --- /dev/null +++ b/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/AmsInboundDetailReportServiceImplTest.java @@ -0,0 +1,75 @@ +package com.ruoyi.asset.service.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import com.ruoyi.asset.constant.InboundOrderStatus; +import com.ruoyi.asset.domain.AmsInboundDetailReport; +import com.ruoyi.asset.mapper.AmsInboundDetailReportMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AmsInboundDetailReportServiceImplTest +{ + @Mock + private AmsInboundDetailReportMapper amsInboundDetailReportMapper; + + @InjectMocks + private AmsInboundDetailReportServiceImpl service; + + /** 报表只能看已完成入库,防止草稿单据影响正式统计口径 */ + @Test + void selectInboundDetailReportListShouldAlwaysUseDoneStatus() + { + AmsInboundDetailReport query = new AmsInboundDetailReport(); + query.setWarehouseId(1L); + when(amsInboundDetailReportMapper.selectInboundDetailReportList(query, InboundOrderStatus.INBOUND_DONE)) + .thenReturn(List.of()); + + service.selectInboundDetailReportList(query); + + ArgumentCaptor reportCaptor = ArgumentCaptor.forClass(AmsInboundDetailReport.class); + verify(amsInboundDetailReportMapper).selectInboundDetailReportList(reportCaptor.capture(), + eq(InboundOrderStatus.INBOUND_DONE)); + assertEquals(1L, reportCaptor.getValue().getWarehouseId()); + } + + @Test + void selectInboundDetailReportListShouldHandleNullQuery() + { + when(amsInboundDetailReportMapper.selectInboundDetailReportList(any(AmsInboundDetailReport.class), + eq(InboundOrderStatus.INBOUND_DONE))).thenReturn(List.of()); + + service.selectInboundDetailReportList(null); + + verify(amsInboundDetailReportMapper).selectInboundDetailReportList(any(AmsInboundDetailReport.class), + eq(InboundOrderStatus.INBOUND_DONE)); + } + + @Test + void selectInboundDetailReportListShouldReturnMapperRows() + { + AmsInboundDetailReport row = new AmsInboundDetailReport(); + row.setInboundNo("RK202606150001"); + row.setAssetCode("ASSET-001"); + row.setInboundQuantity(1); + when(amsInboundDetailReportMapper.selectInboundDetailReportList(any(AmsInboundDetailReport.class), + eq(InboundOrderStatus.INBOUND_DONE))).thenReturn(List.of(row)); + + List result = service.selectInboundDetailReportList(new AmsInboundDetailReport()); + + assertEquals(1, result.size()); + assertEquals("RK202606150001", result.get(0).getInboundNo()); + assertEquals("ASSET-001", result.get(0).getAssetCode()); + assertEquals(1, result.get(0).getInboundQuantity()); + } +}