feat(firstleg): expose smart-loading channel directory
This commit is contained in:
+2
@@ -8,6 +8,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; imp
|
|||||||
@Validated @RestController @RequestMapping("/api/platform/v1/first-leg/smart-loading")
|
@Validated @RestController @RequestMapping("/api/platform/v1/first-leg/smart-loading")
|
||||||
public class PlatformSmartLoadingController {
|
public class PlatformSmartLoadingController {
|
||||||
@Resource private SmartLoadingService service;
|
@Resource private SmartLoadingService service;
|
||||||
|
@GetMapping("/channels") @PreAuthorize("hasAuthority('platform:firstleg:smart-loading:list')")
|
||||||
|
public ApiResponse<List<ChannelSummary>> channels(@AuthenticationPrincipal AuthenticatedUser u,@RequestParam @Positive long warehouseId){FirstlegScopeGuard.platform(u);return ApiResponse.success(service.channels(warehouseId));}
|
||||||
@GetMapping("/candidates") @PreAuthorize("hasAuthority('platform:firstleg:smart-loading:list')")
|
@GetMapping("/candidates") @PreAuthorize("hasAuthority('platform:firstleg:smart-loading:list')")
|
||||||
public ApiResponse<List<Candidate>> candidates(@AuthenticationPrincipal AuthenticatedUser u,@RequestParam @Positive long bookingId,@RequestParam @Positive long warehouseId){FirstlegScopeGuard.platform(u);return ApiResponse.success(service.candidates(bookingId,warehouseId));}
|
public ApiResponse<List<Candidate>> candidates(@AuthenticationPrincipal AuthenticatedUser u,@RequestParam @Positive long bookingId,@RequestParam @Positive long warehouseId){FirstlegScopeGuard.platform(u);return ApiResponse.success(service.candidates(bookingId,warehouseId));}
|
||||||
@GetMapping("/inbound-candidates") @PreAuthorize("hasAuthority('platform:firstleg:smart-loading:list')")
|
@GetMapping("/inbound-candidates") @PreAuthorize("hasAuthority('platform:firstleg:smart-loading:list')")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.baiyue.firstleg.dto;
|
package com.baiyue.firstleg.dto;
|
||||||
import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.util.List;
|
import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.util.List;
|
||||||
public final class SmartLoadingDtos { private SmartLoadingDtos(){}
|
public final class SmartLoadingDtos { private SmartLoadingDtos(){}
|
||||||
|
public record ChannelSummary(String channelCode, String channelName,
|
||||||
|
long candidateInboundCount, long availablePackageCount, long availableContainerCount) {}
|
||||||
public record InboundCandidate(Long accountId, Long inboundId, Long orderId, String orderNo, String inboundCode,
|
public record InboundCandidate(Long accountId, Long inboundId, Long orderId, String orderNo, String inboundCode,
|
||||||
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId,
|
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId,
|
||||||
String destinationWarehouseCode, String destinationWarehouseName, String customerName, String goodsSummary,
|
String destinationWarehouseCode, String destinationWarehouseName, String customerName, String goodsSummary,
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
package com.baiyue.firstleg.mapper;
|
package com.baiyue.firstleg.mapper;
|
||||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.ibatis.annotations.*; import java.util.List;
|
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.ibatis.annotations.*; import java.util.List;
|
||||||
@Mapper public interface SmartLoadingCandidateMapper {
|
@Mapper public interface SmartLoadingCandidateMapper {
|
||||||
|
@Select("""
|
||||||
|
SELECT b.channel_code channelCode,COUNT(DISTINCT b.id) availableContainerCount
|
||||||
|
FROM fl_container_booking b
|
||||||
|
LEFT JOIN fl_loading_plan p ON p.container_booking_id=b.id
|
||||||
|
WHERE b.record_status='ACTIVE' AND b.warehouse_id=#{warehouseId}
|
||||||
|
AND b.channel_code IS NOT NULL AND TRIM(b.channel_code)<>''
|
||||||
|
AND (p.id IS NULL OR p.plan_status IN ('EDITING','LOADING'))
|
||||||
|
GROUP BY b.channel_code
|
||||||
|
""")
|
||||||
|
List<ChannelContainerCountRow> channelContainerCounts(@Param("warehouseId") long warehouseId);
|
||||||
|
|
||||||
@Select("""
|
@Select("""
|
||||||
SELECT o.tenant_id accountId,o.id inboundId,i.inbound_detail_id inboundDetailId,
|
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.order_id orderId,o.order_no orderNo,o.inbound_code inboundCode,
|
||||||
@@ -98,4 +109,9 @@ import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.iba
|
|||||||
""")
|
""")
|
||||||
Candidate candidate(@Param("planId") long planId,@Param("warehouseId") long warehouseId,
|
Candidate candidate(@Param("planId") long planId,@Param("warehouseId") long warehouseId,
|
||||||
@Param("inboundId") long inboundId,@Param("detailId") long detailId);
|
@Param("inboundId") long inboundId,@Param("detailId") long detailId);
|
||||||
|
|
||||||
|
@lombok.Getter @lombok.Setter class ChannelContainerCountRow {
|
||||||
|
private String channelCode;
|
||||||
|
private Long availableContainerCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.baiyue.firstleg.mapper.LoadingPlanHistoryMapper;
|
|||||||
import com.baiyue.firstleg.mapper.LoadingPlanItemMapper;
|
import com.baiyue.firstleg.mapper.LoadingPlanItemMapper;
|
||||||
import com.baiyue.firstleg.mapper.LoadingPlanMapper;
|
import com.baiyue.firstleg.mapper.LoadingPlanMapper;
|
||||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
|
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
|
||||||
|
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.ChannelContainerCountRow;
|
||||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -31,6 +32,9 @@ public class SmartLoadingRepository {
|
|||||||
@Resource private InboundOrderMapper inbounds;
|
@Resource private InboundOrderMapper inbounds;
|
||||||
|
|
||||||
public List<Candidate> candidates(long warehouseId) { return candidates.candidates(warehouseId); }
|
public List<Candidate> candidates(long warehouseId) { return candidates.candidates(warehouseId); }
|
||||||
|
public List<ChannelContainerCountRow> channelContainerCounts(long warehouseId) {
|
||||||
|
return candidates.channelContainerCounts(warehouseId);
|
||||||
|
}
|
||||||
public Candidate candidate(long planId, long warehouseId, long inboundId, long detailId) {
|
public Candidate candidate(long planId, long warehouseId, long inboundId, long detailId) {
|
||||||
return candidates.candidate(planId, warehouseId, inboundId, detailId);
|
return candidates.candidate(planId, warehouseId, inboundId, detailId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,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.GroupSnapshot;
|
||||||
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
||||||
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
|
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
|
||||||
|
import com.baiyue.firstleg.pricebookquery.application.port.in.PriceBookQueryUseCase;
|
||||||
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
@@ -43,11 +44,35 @@ public class SmartLoadingService {
|
|||||||
@Resource private LoadingModelBridgeService loadingModelBridge;
|
@Resource private LoadingModelBridgeService loadingModelBridge;
|
||||||
@Resource private SplitPreloadService splitPreloadService;
|
@Resource private SplitPreloadService splitPreloadService;
|
||||||
@Resource private ColoadGroupAssignmentPort coloadGroups;
|
@Resource private ColoadGroupAssignmentPort coloadGroups;
|
||||||
|
@Resource private PriceBookQueryUseCase priceBooks;
|
||||||
@Resource private FirstlegDocumentNumberGenerator documentNumbers;
|
@Resource private FirstlegDocumentNumberGenerator documentNumbers;
|
||||||
@Resource private ObjectMapper objectMapper;
|
@Resource private ObjectMapper objectMapper;
|
||||||
@Value("${baiyue.security.internal-service-token:}") private String internalToken;
|
@Value("${baiyue.security.internal-service-token:}") private String internalToken;
|
||||||
public SmartLoadingService() { }
|
public SmartLoadingService() { }
|
||||||
|
|
||||||
|
public List<ChannelSummary> channels(long warehouseId) {
|
||||||
|
if (warehouseId <= 0) throw validation("warehouseId必须大于0");
|
||||||
|
Long versionId = priceBooks.activeVersionId();
|
||||||
|
if (versionId == null) return List.of();
|
||||||
|
Map<String, List<Candidate>> candidatesByChannel = repository.candidates(warehouseId).stream()
|
||||||
|
.filter(candidate -> StringUtils.hasText(candidate.channelCode()))
|
||||||
|
.collect(java.util.stream.Collectors.groupingBy(candidate -> candidate.channelCode().trim()));
|
||||||
|
Map<String, Long> containersByChannel = repository.channelContainerCounts(warehouseId).stream()
|
||||||
|
.filter(row -> StringUtils.hasText(row.getChannelCode()))
|
||||||
|
.collect(java.util.stream.Collectors.toMap(row -> row.getChannelCode().trim(),
|
||||||
|
row -> Objects.requireNonNullElse(row.getAvailableContainerCount(), 0L), Long::sum));
|
||||||
|
return priceBooks.channels(versionId, warehouseId).stream().map(channel -> {
|
||||||
|
String channelCode = channel.getChannelCode().trim();
|
||||||
|
List<Candidate> channelCandidates = candidatesByChannel.getOrDefault(channelCode, List.of());
|
||||||
|
long candidateInboundCount = channelCandidates.stream().map(Candidate::inboundId)
|
||||||
|
.filter(Objects::nonNull).distinct().count();
|
||||||
|
long availablePackageCount = channelCandidates.stream().map(Candidate::availablePackageCount)
|
||||||
|
.filter(Objects::nonNull).mapToLong(Integer::longValue).sum();
|
||||||
|
return new ChannelSummary(channelCode, channel.getChannelName(), candidateInboundCount,
|
||||||
|
availablePackageCount, containersByChannel.getOrDefault(channelCode, 0L));
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
public List<Candidate> candidates(long warehouseId) {
|
public List<Candidate> candidates(long warehouseId) {
|
||||||
if (warehouseId <= 0) {
|
if (warehouseId <= 0) {
|
||||||
throw new BizException(ResultCode.BAD_REQUEST, "warehouseId必须大于0");
|
throw new BizException(ResultCode.BAD_REQUEST, "warehouseId必须大于0");
|
||||||
|
|||||||
@@ -382,6 +382,27 @@ paths:
|
|||||||
- {name: bookingId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
- {name: bookingId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||||
- {name: warehouseId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
- {name: warehouseId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||||
responses: {'200': {description: Candidates filtered by the server to the booking channel}}
|
responses: {'200': {description: Candidates filtered by the server to the booking channel}}
|
||||||
|
/api/platform/v1/first-leg/smart-loading/channels:
|
||||||
|
get:
|
||||||
|
summary: 查询当前仓库智能装柜渠道目录及统计
|
||||||
|
description: >-
|
||||||
|
需要 platform:firstleg:smart-loading:list 权限。渠道目录只来自平台当前唯一 ACTIVE 价格版本中
|
||||||
|
对该国内仓存在 ACTIVE 价格行的渠道;候选入仓码数、可用箱数及可编排柜次数由 Firstleg 当前事实聚合。
|
||||||
|
零候选或零可用柜次的有效渠道仍返回,Admin 可展示空态;实际分配仍由服务端重新校验仓库、渠道和资格。
|
||||||
|
parameters:
|
||||||
|
- {name: warehouseId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功;无 ACTIVE 价格版本时 data 为空数组
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code: {type: string}
|
||||||
|
message: {type: string}
|
||||||
|
data: {type: array, items: {$ref: '#/components/schemas/SmartLoadingChannelSummary'}}
|
||||||
|
'403': {description: 无智能装柜查询权限}
|
||||||
/api/platform/v1/first-leg/smart-loading/inbound-candidates:
|
/api/platform/v1/first-leg/smart-loading/inbound-candidates:
|
||||||
get:
|
get:
|
||||||
summary: 按入仓码查询智能装柜候选
|
summary: 按入仓码查询智能装柜候选
|
||||||
@@ -1700,6 +1721,16 @@ components:
|
|||||||
required: [itemId]
|
required: [itemId]
|
||||||
properties:
|
properties:
|
||||||
itemId: {type: string, pattern: '^[1-9][0-9]*$'}
|
itemId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||||
|
SmartLoadingChannelSummary:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [channelCode, channelName, candidateInboundCount, availablePackageCount, availableContainerCount]
|
||||||
|
properties:
|
||||||
|
channelCode: {type: string, minLength: 1, maxLength: 64}
|
||||||
|
channelName: {type: string, minLength: 1, maxLength: 128}
|
||||||
|
candidateInboundCount: {type: integer, format: int64, minimum: 0, description: 当前可分配的唯一入仓码数量}
|
||||||
|
availablePackageCount: {type: integer, format: int64, minimum: 0, description: 当前候选规格剩余可分配箱数合计}
|
||||||
|
availableContainerCount: {type: integer, format: int64, minimum: 0, description: 当前 WAIT_LOADING、EDITING 或 LOADING 可编排柜次数量}
|
||||||
SmartLoadingPlan:
|
SmartLoadingPlan:
|
||||||
type: object
|
type: object
|
||||||
required: [id, planNo, status, bookingId, version, canAssign, canEdit, finalCompletionStatus, items]
|
required: [id, planNo, status, bookingId, version, canAssign, canEdit, finalCompletionStatus, items]
|
||||||
|
|||||||
+4
-1
@@ -12,11 +12,14 @@ class PlatformSmartLoadingControllerContractTest {
|
|||||||
authority("draftByBooking","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
authority("draftByBooking","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||||
authority("detail","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
authority("detail","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||||
authority("candidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
authority("candidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
||||||
authority("inboundCandidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
authority("inboundCandidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
||||||
|
authority("channels","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||||
}
|
}
|
||||||
@Test void openApiRequiresBookingAndWarehouseForCandidates() throws Exception {
|
@Test void openApiRequiresBookingAndWarehouseForCandidates() throws Exception {
|
||||||
String yaml=java.nio.file.Files.readString(java.nio.file.Path.of("src/main/resources/openapi/firstleg-v1.yaml"));
|
String yaml=java.nio.file.Files.readString(java.nio.file.Path.of("src/main/resources/openapi/firstleg-v1.yaml"));
|
||||||
assertThat(yaml).contains("/api/platform/v1/first-leg/smart-loading/candidates:")
|
assertThat(yaml).contains("/api/platform/v1/first-leg/smart-loading/candidates:")
|
||||||
|
.contains("/api/platform/v1/first-leg/smart-loading/channels:")
|
||||||
|
.contains("零候选或零可用柜次的有效渠道仍返回")
|
||||||
.contains("name: bookingId, in: query, required: true")
|
.contains("name: bookingId, in: query, required: true")
|
||||||
.contains("name: warehouseId, in: query, required: true");
|
.contains("name: warehouseId, in: query, required: true");
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -69,4 +69,19 @@ class SmartLoadingCandidateMapperContractTest {
|
|||||||
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
||||||
.contains("FOR UPDATE");
|
.contains("FOR UPDATE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void channelContainerStatisticsCountOnlyActiveOrchestratableContainers() throws Exception {
|
||||||
|
Method method = SmartLoadingCandidateMapper.class.getDeclaredMethod(
|
||||||
|
"channelContainerCounts", long.class);
|
||||||
|
String sql = String.join(" ", method.getAnnotation(Select.class).value());
|
||||||
|
|
||||||
|
assertThat(sql)
|
||||||
|
.contains("b.record_status='ACTIVE'")
|
||||||
|
.contains("b.warehouse_id=#{warehouseId}")
|
||||||
|
.contains("p.id IS NULL OR p.plan_status IN ('EDITING','LOADING')")
|
||||||
|
.contains("COUNT(DISTINCT b.id) availableContainerCount")
|
||||||
|
.contains("GROUP BY b.channel_code")
|
||||||
|
.doesNotContain("'COMPLETED'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService;
|
|||||||
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
|
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
|
||||||
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.GroupSnapshot;
|
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.GroupSnapshot;
|
||||||
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
||||||
|
import com.baiyue.firstleg.pricebookquery.application.model.PriceBookQueryRows.ChannelRow;
|
||||||
|
import com.baiyue.firstleg.pricebookquery.application.port.in.PriceBookQueryUseCase;
|
||||||
|
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.ChannelContainerCountRow;
|
||||||
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import feign.FeignException;
|
import feign.FeignException;
|
||||||
@@ -73,6 +76,7 @@ class SmartLoadingServiceTest {
|
|||||||
@MockBean private LoadingModelBridgeService loadingModelBridge;
|
@MockBean private LoadingModelBridgeService loadingModelBridge;
|
||||||
@MockBean private SplitPreloadService splitPreloadService;
|
@MockBean private SplitPreloadService splitPreloadService;
|
||||||
@MockBean private ColoadGroupAssignmentPort coloadGroups;
|
@MockBean private ColoadGroupAssignmentPort coloadGroups;
|
||||||
|
@MockBean private PriceBookQueryUseCase priceBooks;
|
||||||
@MockBean private FirstlegDocumentNumberGenerator documentNumbers;
|
@MockBean private FirstlegDocumentNumberGenerator documentNumbers;
|
||||||
@Resource private SmartLoadingService service;
|
@Resource private SmartLoadingService service;
|
||||||
|
|
||||||
@@ -139,6 +143,40 @@ class SmartLoadingServiceTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void channelDirectoryKeepsActiveZeroStatChannelAndAggregatesCandidateAndContainerFacts() {
|
||||||
|
ChannelRow maxTruck = channel("MATSON_MAX_TRUCK", "美森加班海卡");
|
||||||
|
ChannelRow clxTruck = channel("MATSON_CLX_TRUCK", "美森正班海卡");
|
||||||
|
Candidate first = candidate(9L, 10L, 20L, 30L, 4);
|
||||||
|
Candidate secondDetail = candidate(9L, 10L, 21L, 30L, 6);
|
||||||
|
Candidate secondInbound = candidate(19L, 11L, 22L, 31L, 5);
|
||||||
|
first = withChannel(first, "MATSON_MAX_TRUCK", "旧渠道快照名称");
|
||||||
|
secondDetail = withChannel(secondDetail, "MATSON_MAX_TRUCK", "旧渠道快照名称");
|
||||||
|
secondInbound = withChannel(secondInbound, "MATSON_MAX_TRUCK", "旧渠道快照名称");
|
||||||
|
ChannelContainerCountRow count = new ChannelContainerCountRow();
|
||||||
|
count.setChannelCode("MATSON_MAX_TRUCK"); count.setAvailableContainerCount(2L);
|
||||||
|
when(priceBooks.activeVersionId()).thenReturn(88L);
|
||||||
|
when(priceBooks.channels(88L, 100L)).thenReturn(List.of(maxTruck, clxTruck));
|
||||||
|
when(repository.candidates(100L)).thenReturn(List.of(first, secondDetail, secondInbound));
|
||||||
|
when(repository.channelContainerCounts(100L)).thenReturn(List.of(count));
|
||||||
|
|
||||||
|
var result = service.channels(100L);
|
||||||
|
|
||||||
|
assertThat(result).hasSize(2);
|
||||||
|
assertThat(result.get(0)).satisfies(channel -> {
|
||||||
|
assertThat(channel.channelName()).isEqualTo("美森加班海卡");
|
||||||
|
assertThat(channel.candidateInboundCount()).isEqualTo(2);
|
||||||
|
assertThat(channel.availablePackageCount()).isEqualTo(15);
|
||||||
|
assertThat(channel.availableContainerCount()).isEqualTo(2);
|
||||||
|
});
|
||||||
|
assertThat(result.get(1)).satisfies(channel -> {
|
||||||
|
assertThat(channel.channelCode()).isEqualTo("MATSON_CLX_TRUCK");
|
||||||
|
assertThat(channel.candidateInboundCount()).isZero();
|
||||||
|
assertThat(channel.availablePackageCount()).isZero();
|
||||||
|
assertThat(channel.availableContainerCount()).isZero();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void exposesWholeColoadGroupFactsOnEveryMemberCandidate() {
|
void exposesWholeColoadGroupFactsOnEveryMemberCandidate() {
|
||||||
Candidate first = candidate(9L, 10L, 20L, 30L, 4);
|
Candidate first = candidate(9L, 10L, 20L, 30L, 4);
|
||||||
@@ -1297,6 +1335,21 @@ class SmartLoadingServiceTest {
|
|||||||
new BigDecimal("0.5"), null, null, null, null, null, null);
|
new BigDecimal("0.5"), null, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Candidate withChannel(Candidate value, String channelCode, String channelName) {
|
||||||
|
return new Candidate(value.accountId(), value.inboundId(), value.inboundDetailId(), value.orderId(),
|
||||||
|
value.orderNo(), value.inboundCode(), value.warehouseId(), channelCode, channelName,
|
||||||
|
value.destinationWarehouseId(), value.destinationWarehouseCode(), value.destinationWarehouseName(),
|
||||||
|
value.customerName(), value.goodsName(), value.availablePackageCount(), value.unitGrossWeightKg(),
|
||||||
|
value.unitNetWeightKg(), value.unitVolumeCbm(), value.coLoadGroupId(), value.groupNo(),
|
||||||
|
value.memberCount(), value.groupTotalBoxes(), value.locked(), value.blockReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChannelRow channel(String channelCode, String channelName) {
|
||||||
|
ChannelRow value = new ChannelRow();
|
||||||
|
value.setChannelCode(channelCode); value.setChannelName(channelName);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private static GroupSnapshot group(String status, Long lockedPlanId, Member... members) {
|
private static GroupSnapshot group(String status, Long lockedPlanId, Member... members) {
|
||||||
GroupSnapshot group = mock(GroupSnapshot.class);
|
GroupSnapshot group = mock(GroupSnapshot.class);
|
||||||
when(group.groupId()).thenReturn(700L);
|
when(group.groupId()).thenReturn(700L);
|
||||||
|
|||||||
Reference in New Issue
Block a user