优化
This commit is contained in:
+105
-24
@@ -1,5 +1,5 @@
|
||||
package com.baiyue.firstleg.mapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainer; import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Set;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import com.baiyue.firstleg.dto.SmartLoadingDtos.InboundCandidateQuery; import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainer; import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery; import org.apache.ibatis.annotations.*; import java.time.LocalDateTime; import java.util.List; import java.util.Set;
|
||||
@Mapper public interface SmartLoadingCandidateMapper {
|
||||
@Select("""
|
||||
SELECT b.channel_code channelCode,COUNT(DISTINCT b.id) availableContainerCount
|
||||
@@ -63,12 +63,83 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
|
||||
List<Candidate> candidates(@Param("warehouseId") long warehouseId);
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT current_group.id groupId,
|
||||
CASE WHEN current_group.id IS NULL THEN eligible.accountId END accountId,
|
||||
CASE WHEN current_group.id IS NULL THEN eligible.inboundId END inboundId,
|
||||
MIN(eligible.expectedArrivalAt) sortArrivalAt,MIN(eligible.inboundId) sortInboundId
|
||||
FROM (
|
||||
SELECT detail.accountId,detail.inboundId,detail.inboundCode,detail.expectedArrivalAt,
|
||||
SUM(detail.availablePackageCount) availablePackageCount
|
||||
FROM (
|
||||
SELECT o.tenant_id accountId,o.id inboundId,o.inbound_code inboundCode,
|
||||
o.expected_arrival_at expectedArrivalAt,i.inbound_detail_id,
|
||||
SUM(i.package_count)
|
||||
-COALESCE((SELECT SUM(li.package_count) FROM fl_loading_plan_item li
|
||||
WHERE li.account_id=o.tenant_id AND li.inbound_id=o.id
|
||||
AND li.inbound_detail_id=i.inbound_detail_id AND li.deleted=0),0)
|
||||
-COALESCE((SELECT SUM(split_item.package_count) FROM fl_inbound_split_item split_item
|
||||
WHERE o.inbound_role='MASTER' AND split_item.tenant_id=o.tenant_id
|
||||
AND split_item.parent_inbound_id=o.id
|
||||
AND split_item.source_inbound_detail_id=i.inbound_detail_id),0) availablePackageCount
|
||||
FROM fl_inbound_order o
|
||||
JOIN fl_inbound_measurement m ON m.tenant_id=o.tenant_id AND m.inbound_id=o.id
|
||||
AND m.status='APPROVED'
|
||||
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
||||
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
||||
JOIN fl_inbound_measurement_item i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id
|
||||
AND i.inbound_detail_id IS NOT NULL AND i.package_count>0
|
||||
WHERE (#{query.warehouseId} IS NULL OR o.warehouse_id=#{query.warehouseId})
|
||||
AND o.channel_code IN
|
||||
<foreach collection="actualChannels" item="channel" open="(" separator="," close=")">#{channel}</foreach>
|
||||
AND o.process_status='WAIT_SPLIT'
|
||||
AND o.actual_gross_weight_kg IS NOT NULL AND o.actual_net_weight_kg IS NOT NULL
|
||||
AND (o.inbound_role='MASTER' OR (o.split_purpose='REMAINDER' AND o.split_status='NOT_SPLIT'))
|
||||
GROUP BY o.tenant_id,o.id,o.inbound_code,o.expected_arrival_at,i.inbound_detail_id
|
||||
HAVING availablePackageCount>0
|
||||
) detail
|
||||
GROUP BY detail.accountId,detail.inboundId,detail.inboundCode,detail.expectedArrivalAt
|
||||
) eligible
|
||||
LEFT JOIN fl_coload_group_member current_member
|
||||
ON current_member.tenant_id=eligible.accountId AND current_member.inbound_id=eligible.inboundId
|
||||
AND current_member.active_flag=1
|
||||
LEFT JOIN fl_coload_group current_group
|
||||
ON current_group.id=current_member.group_id AND current_group.group_status IN ('ACTIVE','LOCKED')
|
||||
GROUP BY current_group.id,
|
||||
CASE WHEN current_group.id IS NULL THEN eligible.accountId END,
|
||||
CASE WHEN current_group.id IS NULL THEN eligible.inboundId END
|
||||
HAVING (current_group.id IS NULL OR (
|
||||
MIN(current_group.group_status)='ACTIVE' AND MAX(current_group.locked_plan_id) IS NULL
|
||||
AND COUNT(*)=(SELECT COUNT(*) FROM fl_coload_group_member group_member_count
|
||||
WHERE group_member_count.group_id=current_group.id AND group_member_count.active_flag=1)
|
||||
AND SUM(CASE WHEN eligible.availablePackageCount=current_member.full_package_count THEN 1 ELSE 0 END)=COUNT(*)
|
||||
))
|
||||
<if test="query.customerId != null">
|
||||
AND SUM(CASE WHEN eligible.accountId=#{query.customerId} THEN 1 ELSE 0 END)>0
|
||||
</if>
|
||||
<if test="query.inboundCode != null and query.inboundCode.trim() != ''">
|
||||
AND SUM(CASE WHEN LOWER(eligible.inboundCode) LIKE CONCAT('%',LOWER(#{query.inboundCode}),'%') THEN 1 ELSE 0 END)>0
|
||||
</if>
|
||||
<if test="query.expectedArrivalStart != null or query.expectedArrivalEnd != null">
|
||||
AND SUM(CASE WHEN eligible.expectedArrivalAt IS NOT NULL
|
||||
<if test="query.expectedArrivalStart != null">AND DATE(eligible.expectedArrivalAt)>=#{query.expectedArrivalStart}</if>
|
||||
<if test="query.expectedArrivalEnd != null">AND DATE(eligible.expectedArrivalAt)<=#{query.expectedArrivalEnd}</if>
|
||||
THEN 1 ELSE 0 END)>0
|
||||
</if>
|
||||
ORDER BY sortArrivalAt IS NULL,sortArrivalAt,sortInboundId
|
||||
</script>
|
||||
""")
|
||||
Page<CandidateUnit> globalCandidateUnits(Page<CandidateUnit> page,
|
||||
@Param("query") InboundCandidateQuery query,@Param("actualChannels") Set<String> actualChannels);
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT o.tenant_id accountId,o.id inboundId,i.inbound_detail_id inboundDetailId,
|
||||
o.order_id orderId,o.order_no orderNo,o.inbound_code inboundCode,
|
||||
o.warehouse_id warehouseId,o.channel_code channelCode,o.channel_name channelName,o.destination_warehouse_id destinationWarehouseId,
|
||||
o.destination_code destinationWarehouseCode,o.destination_name destinationWarehouseName,
|
||||
o.customer_name customerName,COALESCE(m.actual_goods_name,'') goodsName,
|
||||
i.package_count-COALESCE((SELECT SUM(li.package_count) FROM fl_loading_plan_item li
|
||||
SUM(i.package_count)-COALESCE((SELECT SUM(li.package_count) FROM fl_loading_plan_item li
|
||||
WHERE li.account_id=o.tenant_id AND li.inbound_id=o.id
|
||||
AND li.inbound_detail_id=i.inbound_detail_id AND li.deleted=0),0)
|
||||
-COALESCE((SELECT SUM(split_item.package_count) FROM fl_inbound_split_item split_item
|
||||
@@ -77,36 +148,44 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
|
||||
AND split_item.source_inbound_detail_id=i.inbound_detail_id),0) availablePackageCount,
|
||||
COALESCE(o.actual_gross_weight_kg / NULLIF(o.total_package_count, 0), 0) unitGrossWeightKg,
|
||||
COALESCE(o.actual_net_weight_kg / NULLIF(o.total_package_count, 0), 0) unitNetWeightKg,
|
||||
COALESCE(i.unit_volume_cbm,0) unitVolumeCbm,
|
||||
NULL coLoadGroupId,NULL groupNo,NULL memberCount,NULL groupTotalBoxes,
|
||||
NULL locked,NULL blockReason,o.order_status_snapshot orderStatusSnapshot,o.expected_arrival_at expectedArrivalAt
|
||||
COALESCE(SUM(COALESCE(i.total_volume_cbm,COALESCE(i.unit_volume_cbm,0)*i.package_count))
|
||||
/ NULLIF(SUM(i.package_count),0),0) unitVolumeCbm,
|
||||
current_group.id coLoadGroupId,current_group.group_no groupNo,
|
||||
(SELECT COUNT(*) FROM fl_coload_group_member member_count
|
||||
WHERE member_count.group_id=current_group.id AND member_count.active_flag=1) memberCount,
|
||||
(SELECT SUM(member_boxes.full_package_count) FROM fl_coload_group_member member_boxes
|
||||
WHERE member_boxes.group_id=current_group.id AND member_boxes.active_flag=1) groupTotalBoxes,
|
||||
CASE WHEN current_group.id IS NULL THEN NULL ELSE FALSE END locked,NULL blockReason,
|
||||
o.order_status_snapshot orderStatusSnapshot,o.expected_arrival_at expectedArrivalAt
|
||||
FROM fl_inbound_order o
|
||||
JOIN fl_inbound_measurement m ON m.tenant_id=o.tenant_id AND m.inbound_id=o.id
|
||||
AND m.status='APPROVED'
|
||||
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
||||
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
||||
JOIN (
|
||||
SELECT tenant_id,measurement_id,inbound_detail_id,SUM(package_count) package_count,
|
||||
COALESCE(SUM(COALESCE(total_volume_cbm,COALESCE(unit_volume_cbm,0)*package_count))
|
||||
/ NULLIF(SUM(package_count),0),0) unit_volume_cbm
|
||||
FROM fl_inbound_measurement_item
|
||||
WHERE inbound_detail_id IS NOT NULL AND package_count>0
|
||||
GROUP BY tenant_id,measurement_id,inbound_detail_id
|
||||
) i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id
|
||||
WHERE (#{warehouseId} IS NULL OR o.warehouse_id=#{warehouseId})
|
||||
AND o.process_status='WAIT_SPLIT'
|
||||
JOIN fl_inbound_measurement_item i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id
|
||||
AND i.inbound_detail_id IS NOT NULL AND i.package_count>0
|
||||
LEFT JOIN fl_coload_group_member current_member
|
||||
ON current_member.tenant_id=o.tenant_id AND current_member.inbound_id=o.id AND current_member.active_flag=1
|
||||
LEFT JOIN fl_coload_group current_group
|
||||
ON current_group.id=current_member.group_id AND current_group.group_status='ACTIVE'
|
||||
AND current_group.locked_plan_id IS NULL
|
||||
WHERE o.process_status='WAIT_SPLIT'
|
||||
AND o.actual_gross_weight_kg IS NOT NULL AND o.actual_net_weight_kg IS NOT NULL
|
||||
AND (o.inbound_role='MASTER' OR (o.split_purpose='REMAINDER' AND o.split_status='NOT_SPLIT'))
|
||||
AND i.package_count>COALESCE((SELECT SUM(li.package_count) FROM fl_loading_plan_item li
|
||||
WHERE li.account_id=o.tenant_id AND li.inbound_id=o.id
|
||||
AND li.inbound_detail_id=i.inbound_detail_id AND li.deleted=0),0)
|
||||
+COALESCE((SELECT SUM(split_item.package_count) FROM fl_inbound_split_item split_item
|
||||
WHERE o.inbound_role='MASTER' AND split_item.tenant_id=o.tenant_id
|
||||
AND split_item.parent_inbound_id=o.id
|
||||
AND split_item.source_inbound_detail_id=i.inbound_detail_id),0)
|
||||
ORDER BY o.inbounded_at ASC, o.id ASC, i.inbound_detail_id ASC
|
||||
AND (
|
||||
<foreach collection="units" item="unit" separator=" OR ">
|
||||
<choose>
|
||||
<when test="unit.groupId != null">current_group.id=#{unit.groupId}</when>
|
||||
<otherwise>(current_group.id IS NULL AND o.tenant_id=#{unit.accountId} AND o.id=#{unit.inboundId})</otherwise>
|
||||
</choose>
|
||||
</foreach>
|
||||
)
|
||||
GROUP BY o.id,m.id,i.inbound_detail_id,current_group.id,current_group.group_no
|
||||
HAVING availablePackageCount>0
|
||||
ORDER BY o.inbounded_at ASC,o.id ASC,i.inbound_detail_id ASC
|
||||
</script>
|
||||
""")
|
||||
List<Candidate> globalCandidates(@Param("warehouseId") Long warehouseId);
|
||||
List<Candidate> globalCandidatesByUnits(@Param("units") List<CandidateUnit> units);
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
@@ -205,4 +284,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
|
||||
private String channelCode;
|
||||
private Long availableContainerCount;
|
||||
}
|
||||
record CandidateUnit(Long groupId,Long accountId,Long inboundId,
|
||||
LocalDateTime sortArrivalAt,Long sortInboundId) {}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.InboundCandidateQuery;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainer;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
@@ -17,6 +18,7 @@ import com.baiyue.firstleg.mapper.LoadingPlanItemMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingPlanMapper;
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.ChannelContainerCountRow;
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.CandidateUnit;
|
||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -37,7 +39,14 @@ public class SmartLoadingRepository {
|
||||
@Resource private InboundOrderMapper inbounds;
|
||||
|
||||
public List<Candidate> candidates(long warehouseId) { return candidates.candidates(warehouseId); }
|
||||
public List<Candidate> globalCandidates(Long warehouseId) { return candidates.globalCandidates(warehouseId); }
|
||||
public PageResult<CandidateUnit> globalCandidateUnits(InboundCandidateQuery query,
|
||||
Set<String> actualChannels) {
|
||||
return PageResult.from(candidates.globalCandidateUnits(
|
||||
new Page<>(query.page(), query.size()), query, actualChannels));
|
||||
}
|
||||
public List<Candidate> globalCandidatesByUnits(List<CandidateUnit> units) {
|
||||
return units.isEmpty() ? List.of() : candidates.globalCandidatesByUnits(units);
|
||||
}
|
||||
public PageResult<WorkbenchContainer> workbenchContainers(WorkbenchContainerQuery query,
|
||||
Set<String> actualChannels) {
|
||||
return PageResult.from(candidates.workbenchContainers(
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService;
|
||||
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.GroupSnapshot;
|
||||
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
||||
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.CandidateUnit;
|
||||
import com.baiyue.firstleg.pricebookquery.application.port.in.PriceBookQueryUseCase;
|
||||
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
@@ -109,46 +110,42 @@ public class SmartLoadingService {
|
||||
}
|
||||
|
||||
/** Global workbench candidates. A co-load group is one paging unit and can never be split across pages. */
|
||||
@Transactional(readOnly = true)
|
||||
public PageResult<InboundCandidate> inboundCandidates(InboundCandidateQuery query) {
|
||||
if (query.expectedArrivalStart() != null && query.expectedArrivalEnd() != null
|
||||
&& query.expectedArrivalStart().isAfter(query.expectedArrivalEnd())) {
|
||||
throw validation("预计到货开始日期不能晚于结束日期");
|
||||
}
|
||||
Set<String> actualChannels = actualChannels(query.channelGroupCodes());
|
||||
List<InboundCandidate> all = repository.globalCandidates(query.warehouseId()).stream()
|
||||
if (query.orderStatus() != null && query.orderStatus() != CandidateOrderStatus.WAIT_SPLIT) {
|
||||
return new PageResult<>(List.of(), query.page(), query.size(), 0, 0);
|
||||
}
|
||||
PageResult<CandidateUnit> unitPage = repository.globalCandidateUnits(query, actualChannels);
|
||||
if (unitPage.records().isEmpty()) {
|
||||
return new PageResult<>(List.of(), unitPage.page(), unitPage.size(), unitPage.total(), unitPage.pages());
|
||||
}
|
||||
List<InboundCandidate> all = repository.globalCandidatesByUnits(unitPage.records()).stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(Candidate::inboundId, LinkedHashMap::new,
|
||||
java.util.stream.Collectors.toList()))
|
||||
.values().stream().map(this::inboundCandidate).toList();
|
||||
Map<String, GroupSnapshot> snapshots = new HashMap<>();
|
||||
Map<Long, List<InboundCandidate>> grouped = new LinkedHashMap<>();
|
||||
List<InboundCandidate> standalone = new ArrayList<>();
|
||||
Map<String, InboundCandidate> units = new HashMap<>();
|
||||
Map<Long, List<InboundCandidate>> grouped = all.stream()
|
||||
.filter(candidate -> candidate.coLoadGroupId() != null)
|
||||
.collect(java.util.stream.Collectors.groupingBy(InboundCandidate::coLoadGroupId,
|
||||
LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
||||
for (InboundCandidate candidate : all) {
|
||||
GroupSnapshot group = snapshots.computeIfAbsent(candidate.accountId() + "|" + candidate.inboundId(),
|
||||
ignored -> group(candidate.accountId(), candidate.inboundId()));
|
||||
if (group == null) {
|
||||
standalone.add(candidate);
|
||||
} else {
|
||||
grouped.computeIfAbsent(group.groupId(), ignored -> new ArrayList<>()).add(candidate);
|
||||
if (candidate.coLoadGroupId() == null) {
|
||||
units.put(unitKey(null, candidate.accountId(), candidate.inboundId()), candidate);
|
||||
}
|
||||
}
|
||||
List<InboundCandidate> units = new ArrayList<>(standalone);
|
||||
for (List<InboundCandidate> members : grouped.values()) {
|
||||
GroupSnapshot group = snapshots.get(members.get(0).accountId() + "|" + members.get(0).inboundId());
|
||||
InboundCandidate aggregate = globalGroupCandidate(group, members);
|
||||
if (aggregate != null) units.add(aggregate);
|
||||
for (var entry : grouped.entrySet()) {
|
||||
InboundCandidate aggregate = globalGroupCandidate(entry.getValue());
|
||||
if (aggregate != null) units.put(unitKey(entry.getKey(), null, null), aggregate);
|
||||
}
|
||||
units = units.stream().filter(candidate -> matches(query, actualChannels, candidate))
|
||||
.sorted(Comparator.comparing(InboundCandidate::expectedArrivalAt,
|
||||
Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
.thenComparing(InboundCandidate::inboundId, Comparator.nullsLast(Comparator.naturalOrder())))
|
||||
.toList();
|
||||
long total = units.size();
|
||||
long offset = query.page() - 1 > Long.MAX_VALUE / query.size()
|
||||
? Long.MAX_VALUE : (query.page() - 1) * query.size();
|
||||
long from = Math.min(total, offset);
|
||||
long to = Math.min(total, from + query.size());
|
||||
long pages = total == 0 ? 0 : (total + query.size() - 1) / query.size();
|
||||
return new PageResult<>(units.subList((int) from, (int) to), query.page(), query.size(), total, pages);
|
||||
List<InboundCandidate> records = unitPage.records().stream()
|
||||
.map(unit -> units.get(unitKey(unit.groupId(), unit.accountId(), unit.inboundId())))
|
||||
.filter(Objects::nonNull).toList();
|
||||
return new PageResult<>(records, unitPage.page(), unitPage.size(), unitPage.total(), unitPage.pages());
|
||||
}
|
||||
|
||||
public PageResult<WorkbenchContainer> containers(WorkbenchContainerQuery query) {
|
||||
@@ -180,18 +177,16 @@ public class SmartLoadingService {
|
||||
first.expectedArrivalAt(), List.of());
|
||||
}
|
||||
|
||||
private InboundCandidate globalGroupCandidate(GroupSnapshot group, List<InboundCandidate> availableMembers) {
|
||||
if (group == null || !"ACTIVE".equals(group.status()) || group.lockedPlanId() != null) return null;
|
||||
Map<Long, InboundCandidate> byInbound = availableMembers.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(InboundCandidate::inboundId, value -> value, (a, b) -> a));
|
||||
for (Member member : group.members()) {
|
||||
InboundCandidate candidate = byInbound.get(member.inboundId());
|
||||
if (candidate == null || !Objects.equals(candidate.accountId(), member.tenantId())
|
||||
|| !Objects.equals(candidate.availablePackageCount(), member.fullPackageCount())) return null;
|
||||
}
|
||||
List<InboundCandidate> ordered = group.members().stream().map(member -> byInbound.get(member.inboundId())).toList();
|
||||
private InboundCandidate globalGroupCandidate(List<InboundCandidate> availableMembers) {
|
||||
if (availableMembers.isEmpty()) return null;
|
||||
InboundCandidate metadata = availableMembers.get(0);
|
||||
if (metadata.coLoadGroupId() == null || metadata.memberCount() == null
|
||||
|| metadata.groupTotalBoxes() == null || availableMembers.size() != metadata.memberCount()) return null;
|
||||
List<InboundCandidate> ordered = availableMembers.stream()
|
||||
.sorted(Comparator.comparing(InboundCandidate::inboundId)).toList();
|
||||
InboundCandidate first = ordered.get(0);
|
||||
int packages = ordered.stream().map(InboundCandidate::availablePackageCount).mapToInt(Integer::intValue).sum();
|
||||
if (packages != metadata.groupTotalBoxes()) return null;
|
||||
BigDecimal volume = ordered.stream().map(InboundCandidate::availableVolumeCbm)
|
||||
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(4, RoundingMode.HALF_UP);
|
||||
BigDecimal gross = weightedAverage(ordered, InboundCandidate::unitGrossWeightKg, packages);
|
||||
@@ -209,12 +204,16 @@ public class SmartLoadingService {
|
||||
return new InboundCandidate(first.accountId(), first.inboundId(), first.orderId(), first.orderNo(),
|
||||
first.inboundCode(), first.warehouseId(), first.channelCode(), first.channelName(),
|
||||
first.destinationWarehouseId(), first.destinationWarehouseCode(), first.destinationWarehouseName(),
|
||||
customers, goods, packages, gross, net, averageVolume, volume, group.groupId(), group.groupNo(),
|
||||
group.members().size(), group.fullPackageCount(), false, null, first.orderStatusSnapshot(),
|
||||
customers, goods, packages, gross, net, averageVolume, volume, metadata.coLoadGroupId(), metadata.groupNo(),
|
||||
metadata.memberCount(), metadata.groupTotalBoxes(), false, null, first.orderStatusSnapshot(),
|
||||
ordered.stream().map(InboundCandidate::expectedArrivalAt).filter(Objects::nonNull).min(LocalDateTime::compareTo).orElse(null),
|
||||
groupMembers);
|
||||
}
|
||||
|
||||
private static String unitKey(Long groupId, Long accountId, Long inboundId) {
|
||||
return groupId == null ? "I|" + accountId + '|' + inboundId : "G|" + groupId;
|
||||
}
|
||||
|
||||
private static BigDecimal weightedAverage(List<InboundCandidate> members,
|
||||
java.util.function.Function<InboundCandidate, BigDecimal> getter,
|
||||
int packages) {
|
||||
@@ -236,36 +235,6 @@ public class SmartLoadingService {
|
||||
return actual;
|
||||
}
|
||||
|
||||
private static boolean matches(InboundCandidateQuery query, Set<String> actualChannels,
|
||||
InboundCandidate candidate) {
|
||||
if (!actualChannels.contains(candidate.channelCode())) return false;
|
||||
if (query.customerId() != null && !Objects.equals(candidate.accountId(), query.customerId())
|
||||
&& candidate.groupMembers().stream().noneMatch(
|
||||
member -> Objects.equals(member.accountId(), query.customerId()))) return false;
|
||||
String inboundCode = trim(query.inboundCode());
|
||||
if (StringUtils.hasText(inboundCode) && candidate.groupMembers().stream()
|
||||
.map(GroupMemberCandidate::inboundCode).filter(Objects::nonNull)
|
||||
.noneMatch(value -> value.toLowerCase(Locale.ROOT).contains(inboundCode.toLowerCase(Locale.ROOT)))
|
||||
&& (candidate.inboundCode() == null || !candidate.inboundCode().toLowerCase(Locale.ROOT)
|
||||
.contains(inboundCode.toLowerCase(Locale.ROOT)))) return false;
|
||||
if (query.expectedArrivalStart() != null || query.expectedArrivalEnd() != null) {
|
||||
boolean arrivalMatches = candidate.groupMembers().isEmpty()
|
||||
? inArrivalRange(candidate.expectedArrivalAt(), query)
|
||||
: candidate.groupMembers().stream().anyMatch(member -> inArrivalRange(
|
||||
member.expectedArrivalAt(), query));
|
||||
if (!arrivalMatches) return false;
|
||||
}
|
||||
return query.orderStatus() == null || query.orderStatus() == CandidateOrderStatus.WAIT_SPLIT;
|
||||
}
|
||||
|
||||
private static boolean inArrivalRange(LocalDateTime arrival, InboundCandidateQuery query) {
|
||||
if (arrival == null) return false;
|
||||
return (query.expectedArrivalStart() == null
|
||||
|| !arrival.toLocalDate().isBefore(query.expectedArrivalStart()))
|
||||
&& (query.expectedArrivalEnd() == null
|
||||
|| !arrival.toLocalDate().isAfter(query.expectedArrivalEnd()));
|
||||
}
|
||||
|
||||
public Plan draftByBooking(long bookingId) {
|
||||
ContainerBookingEntity booking = requireBooking(bookingId);
|
||||
LoadingPlanEntity plan = repository.planByBooking(bookingId);
|
||||
|
||||
Reference in New Issue
Block a user