This commit is contained in:
parent
1a942d9370
commit
3dce4e1868
|
|
@ -18,4 +18,8 @@ public interface DrpCheckInventoryMapper extends BaseMapper<DrpCheckInventoryPag
|
|||
List<Map<String, Object>> selectInventoryByPrice(@Param("materialIds") List<Long> materialIds,
|
||||
@Param("secondCheckDate") String secondCheckDate,
|
||||
@Param("warehouseId") String warehouseId);
|
||||
|
||||
List<Map<String, Object>> selectActualNumFromInventoryBase(@Param("materialIds") List<Long> materialIds,
|
||||
@Param("warehouseId") String warehouseId,
|
||||
@Param("secondCheckDate") String secondCheckDate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -40,75 +43,190 @@ public class DrpCheckInventoryServiceImp implements DrpCheckInventoryService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<DrpCheckDetailVO> getDrpCheckInventoryDetail(DrpCheckDetailVO leRequest) {
|
||||
public List<DrpCheckDetailVO> getDrpCheckInventoryDetail(DrpCheckDetailVO leRequest)
|
||||
{
|
||||
List<String> collect = Collections.singletonList(leRequest.getCheckId());
|
||||
List<DrpCheckDetailVO> detailList = mapper.selectListByIntoIds(collect);
|
||||
if (detailList.isEmpty()) return Collections.emptyList();
|
||||
|
||||
// 1. 查询 materialId 列表
|
||||
// 1. materialId 列表
|
||||
List<Long> materialIds = detailList.stream()
|
||||
.map(DrpCheckDetailVO::getMaterialId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if(materialIds.isEmpty()) return Collections.emptyList();
|
||||
if (materialIds.isEmpty()) return Collections.emptyList();
|
||||
|
||||
// 2. 查询按价格聚合的账面数
|
||||
List<Map<String, Object>> priceBuckets = mapper.selectInventoryByPrice(materialIds, leRequest.getSecondCheckDate(), leRequest.getWarehouseId());
|
||||
// 2. 查询账面数(按单价分组)
|
||||
List<Map<String, Object>> priceBuckets =
|
||||
mapper.selectInventoryByPrice(materialIds, leRequest.getSecondCheckDate(), leRequest.getWarehouseId());
|
||||
|
||||
if(priceBuckets.isEmpty()) return Collections.emptyList();
|
||||
if (priceBuckets == null || priceBuckets.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 3. 遍历 priceBuckets 生成最终列表
|
||||
List<DrpCheckDetailVO> finalList = new ArrayList<>();
|
||||
// 3. 按物料ID汇总实盘总数(来自 drp_check_detail.actual_num)
|
||||
Map<Long, BigDecimal> actualTotalByMaterial = new HashMap<>();
|
||||
for (DrpCheckDetailVO detail : detailList) {
|
||||
if (detail.getMaterialId() != null) {
|
||||
BigDecimal a = detail.getActualNum() == null ? BigDecimal.ZERO : detail.getActualNum();
|
||||
actualTotalByMaterial.put(detail.getMaterialId(),
|
||||
actualTotalByMaterial.getOrDefault(detail.getMaterialId(), BigDecimal.ZERO).add(a));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 按物料分组 priceBuckets
|
||||
Map<Long, List<Map<String, Object>>> bucketsByMaterial = new HashMap<>();
|
||||
for (Map<String, Object> bucket : priceBuckets) {
|
||||
Long materialId = bucket.get("material_id") != null ? ((Number) bucket.get("material_id")).longValue() : 0L;
|
||||
Integer unitPrice = bucket.get("unit_price") != null ? ((Number) bucket.get("unit_price")).intValue() : 0;
|
||||
BigDecimal bookNum = bucket.get("book_num_by_price") != null ? (BigDecimal) bucket.get("book_num_by_price") : BigDecimal.ZERO;
|
||||
Long materialId = ((Number) bucket.get("material_id")).longValue();
|
||||
bucketsByMaterial.computeIfAbsent(materialId, k -> new ArrayList<>()).add(bucket);
|
||||
}
|
||||
|
||||
// 5. 为每个物料排序价格桶 —— 按先进先出时间排序
|
||||
for (Map.Entry<Long, List<Map<String, Object>>> e : bucketsByMaterial.entrySet()) {
|
||||
List<Map<String, Object>> buckets = e.getValue();
|
||||
|
||||
buckets.sort((m1, m2) -> {
|
||||
// 获取时间对象
|
||||
Object t1Obj = m1.get("first_operate_time");
|
||||
Object t2Obj = m2.get("first_operate_time");
|
||||
|
||||
// 转换为Instant
|
||||
Instant t1 = toInstant(t1Obj);
|
||||
Instant t2 = toInstant(t2Obj);
|
||||
|
||||
if (t1 != null && t2 != null) {
|
||||
return t1.compareTo(t2);
|
||||
} else if (t1 != null) {
|
||||
return -1;
|
||||
} else if (t2 != null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 如果都为null,则按单价排序
|
||||
BigDecimal p1 = new BigDecimal(m1.get("unit_price").toString());
|
||||
BigDecimal p2 = new BigDecimal(m2.get("unit_price").toString());
|
||||
return p1.compareTo(p2);
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 计算每个 price-bucket 的账面总和(按物料)
|
||||
Map<Long, BigDecimal> totalBookNumByMaterial = new HashMap<>();
|
||||
Map<Long, BigDecimal> totalBookAmountByMaterial = new HashMap<>();
|
||||
for (Map<String, Object> b : priceBuckets) {
|
||||
Long mid = ((Number) b.get("material_id")).longValue();
|
||||
BigDecimal bn = new BigDecimal(b.get("book_num_by_price").toString());
|
||||
BigDecimal up = new BigDecimal(b.get("unit_price").toString());
|
||||
|
||||
totalBookNumByMaterial.put(mid, totalBookNumByMaterial.getOrDefault(mid, BigDecimal.ZERO).add(bn));
|
||||
totalBookAmountByMaterial.put(mid, totalBookAmountByMaterial.getOrDefault(mid, BigDecimal.ZERO).add(bn.multiply(up)));
|
||||
}
|
||||
|
||||
// 7. 真正按先进先出原则分配实盘数
|
||||
Map<String, BigDecimal> allocatedActualNum = new HashMap<>();
|
||||
|
||||
for (Map.Entry<Long, List<Map<String, Object>>> entry : bucketsByMaterial.entrySet()) {
|
||||
Long materialId = entry.getKey();
|
||||
List<Map<String, Object>> buckets = entry.getValue();
|
||||
|
||||
BigDecimal remainingActual = actualTotalByMaterial.getOrDefault(materialId, BigDecimal.ZERO);
|
||||
|
||||
for (Map<String, Object> bucket : buckets) {
|
||||
if (remainingActual.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
// 实盘数已分配完,后续批次分配0
|
||||
BigDecimal unitPrice = new BigDecimal(bucket.get("unit_price").toString());
|
||||
String key = materialId + "_" + unitPrice.toPlainString();
|
||||
allocatedActualNum.put(key, BigDecimal.ZERO);
|
||||
continue;
|
||||
}
|
||||
|
||||
BigDecimal bucketBookNum = new BigDecimal(bucket.get("book_num_by_price").toString());
|
||||
BigDecimal unitPrice = new BigDecimal(bucket.get("unit_price").toString());
|
||||
String key = materialId + "_" + unitPrice.toPlainString();
|
||||
BigDecimal positiveBook = bucketBookNum.max(BigDecimal.ZERO);
|
||||
BigDecimal alloc = remainingActual.min(positiveBook);
|
||||
remainingActual = remainingActual.subtract(alloc);
|
||||
allocatedActualNum.put(key, alloc);
|
||||
}
|
||||
|
||||
// 如果还有剩余实盘数(实盘数 > 账面总数),这部分不分配到任何价格桶
|
||||
// 页面只显示已分配的部分,多余的实盘数不会显示
|
||||
}
|
||||
|
||||
// 8. 构建最终列表 - 只生成PRICE_BUCKET行
|
||||
Map<Long, DrpCheckDetailVO> sampleDetailByMaterial = detailList.stream()
|
||||
.filter(d -> d.getMaterialId() != null)
|
||||
.collect(Collectors.toMap(DrpCheckDetailVO::getMaterialId, d -> d, (a, b) -> a));
|
||||
|
||||
List<DrpCheckDetailVO> finalList = new ArrayList<>();
|
||||
|
||||
// 按物料ID排序,然后按单价排序
|
||||
priceBuckets.sort((m1, m2) -> {
|
||||
Long a1 = ((Number) m1.get("material_id")).longValue();
|
||||
Long a2 = ((Number) m2.get("material_id")).longValue();
|
||||
int c = a1.compareTo(a2);
|
||||
if (c != 0) return c;
|
||||
|
||||
BigDecimal p1 = new BigDecimal(m1.get("unit_price").toString());
|
||||
BigDecimal p2 = new BigDecimal(m2.get("unit_price").toString());
|
||||
return p1.compareTo(p2);
|
||||
});
|
||||
|
||||
for (Map<String, Object> b : priceBuckets) {
|
||||
Long materialId = ((Number) b.get("material_id")).longValue();
|
||||
BigDecimal unitPrice = new BigDecimal(b.get("unit_price").toString());
|
||||
BigDecimal bookNum = new BigDecimal(b.get("book_num_by_price").toString());
|
||||
String key = materialId + "_" + unitPrice.toPlainString();
|
||||
BigDecimal alloc = allocatedActualNum.getOrDefault(key, BigDecimal.ZERO);
|
||||
|
||||
DrpCheckDetailVO vo = new DrpCheckDetailVO();
|
||||
vo.setMaterialId(materialId);
|
||||
vo.setPrice(unitPrice);
|
||||
vo.setBookNum(bookNum);
|
||||
vo.setBookAmount(unitPrice * bookNum.intValue());
|
||||
vo.setRowType("PRICE_BUCKET");
|
||||
vo.setMaterialId(materialId);
|
||||
vo.setPrice(unitPrice.intValue());
|
||||
vo.setBookNum(bookNum);
|
||||
vo.setBookAmount(bookNum.multiply(unitPrice).intValue());
|
||||
|
||||
// 从 detailList 找到任意一个对应 materialId 的记录填充其他信息
|
||||
detailList.stream()
|
||||
.filter(d -> d.getMaterialId() != null && d.getMaterialId().equals(materialId))
|
||||
.findFirst()
|
||||
.ifPresent(d -> {
|
||||
vo.setDetailId(d.getDetailId());
|
||||
vo.setCheckId(d.getCheckId());
|
||||
vo.setInventoryId(d.getInventoryId());
|
||||
vo.setMaterialCode(d.getMaterialCode());
|
||||
vo.setMaterialName(d.getMaterialName());
|
||||
vo.setCategoryId(d.getCategoryId());
|
||||
vo.setCategoryName(d.getCategoryName());
|
||||
vo.setUnitId(d.getUnitId());
|
||||
vo.setUnitName(d.getUnitName());
|
||||
vo.setImageUrl(d.getImageUrl());
|
||||
vo.setSize(d.getSize());
|
||||
vo.setActualNum(d.getActualNum());
|
||||
vo.setDifferNum(d.getDifferNum());
|
||||
vo.setDifferAmount(d.getDifferAmount());
|
||||
vo.setActualAmount(d.getActualAmount());
|
||||
vo.setDifferReason(d.getDifferReason());
|
||||
vo.setCheckPicUrls(d.getCheckPicUrls());
|
||||
vo.setCheckPicUrlList(d.getCheckPicUrlList());
|
||||
});
|
||||
vo.setActualNum(alloc);
|
||||
vo.setDifferNum(alloc.subtract(bookNum));
|
||||
vo.setActualAmount(alloc.multiply(unitPrice).intValue());
|
||||
vo.setDifferAmount(alloc.multiply(unitPrice).intValue() - bookNum.multiply(unitPrice).intValue());
|
||||
|
||||
// 填基础字段
|
||||
DrpCheckDetailVO sample = sampleDetailByMaterial.get(materialId);
|
||||
if (sample != null) {
|
||||
vo.setDetailId(sample.getDetailId());
|
||||
vo.setCheckId(sample.getCheckId());
|
||||
vo.setInventoryId(sample.getInventoryId());
|
||||
vo.setMaterialCode(sample.getMaterialCode());
|
||||
vo.setMaterialName(sample.getMaterialName());
|
||||
vo.setCategoryId(sample.getCategoryId());
|
||||
vo.setCategoryName(sample.getCategoryName());
|
||||
vo.setUnitId(sample.getUnitId());
|
||||
vo.setUnitName(sample.getUnitName());
|
||||
vo.setImageUrl(sample.getImageUrl());
|
||||
vo.setSize(sample.getSize());
|
||||
vo.setDifferReason(sample.getDifferReason());
|
||||
vo.setCheckPicUrls(sample.getCheckPicUrls());
|
||||
vo.setCheckPicUrlList(sample.getCheckPicUrlList());
|
||||
}
|
||||
|
||||
finalList.add(vo);
|
||||
}
|
||||
|
||||
// 4. 排序:materialId + PRICE_BUCKET 按 unitPrice 升序,null 排到最后
|
||||
finalList.sort(Comparator
|
||||
.comparing(DrpCheckDetailVO::getMaterialId, Comparator.nullsLast(Long::compareTo))
|
||||
.thenComparing(DrpCheckDetailVO::getPrice, Comparator.nullsLast(Integer::compareTo))
|
||||
);
|
||||
|
||||
return finalList;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private static Instant toInstant(Object date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
if (date instanceof java.util.Date) {
|
||||
return ((java.util.Date) date).toInstant();
|
||||
}
|
||||
if (date instanceof java.time.LocalDateTime) {
|
||||
return ((java.time.LocalDateTime) date).atZone(ZoneId.systemDefault()).toInstant();
|
||||
}
|
||||
// 如果有其他类型,可以继续添加
|
||||
throw new IllegalArgumentException("Unsupported date type: " + date.getClass());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,19 +103,25 @@
|
|||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 按单价聚合账面数量 -->
|
||||
<!-- 按单价聚合账面数量(并返回最早的操作时间用于 FIFO 排序) -->
|
||||
<select id="selectInventoryByPrice" resultType="map">
|
||||
SELECT material_id, unit_price,
|
||||
SELECT
|
||||
material_id,
|
||||
unit_price,
|
||||
SUM(CASE WHEN record_type = 1 THEN out_into_num
|
||||
WHEN record_type = 2 THEN -out_into_num ELSE 0 END) AS book_num_by_price
|
||||
WHEN record_type = 2 THEN -out_into_num ELSE 0 END) AS book_num_by_price,
|
||||
MIN(operate_time) AS first_operate_time
|
||||
FROM report_drp_inventory_base
|
||||
WHERE material_id IN
|
||||
<foreach collection="materialIds" item="mid" open="(" close=")" separator=",">
|
||||
#{mid}
|
||||
</foreach>
|
||||
AND operate_time <= #{secondCheckDate} and warehouse_id = #{warehouseId}
|
||||
AND operate_time <= #{secondCheckDate}
|
||||
AND warehouse_id = #{warehouseId}
|
||||
GROUP BY material_id, unit_price
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue