fix: expose phase2 terminal inbound billing
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ public class MerchantInboundBillingController {
|
||||
@GetMapping("/{orderId}/inbound-billing")
|
||||
@PreAuthorize("hasAuthority('merchant:order:view')")
|
||||
@Operation(summary = "查询订单入库结果和最新费用",
|
||||
description = "客户只读查看客服审核通过后发布的费用,不需要确认费用。")
|
||||
description = "客户只读查看已达到 WAIT_SPLIT + APPROVED + CONFIRMED 的二期费用,不需要确认费用。")
|
||||
public ApiResponse<MerchantInboundBillingResponse> find(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long orderId) {
|
||||
return ApiResponse.success(service.find(FirstlegScopeGuard.merchant(user), orderId));
|
||||
|
||||
@@ -20,6 +20,8 @@ public final class InboundProcessStatus {
|
||||
public static final String INBOUNDING = "INBOUNDING";
|
||||
/** 已入库完成,费用已发送给客户。 */
|
||||
public static final String INBOUNDED = "INBOUNDED";
|
||||
/** 二期处理完成,等待三期分柜。 */
|
||||
public static final String WAIT_SPLIT = "WAIT_SPLIT";
|
||||
/** 已进入装柜作业,PDA 可执行拣货。 */
|
||||
public static final String LOADING = "LOADING";
|
||||
/** 装柜完成,等待派送完成。 */
|
||||
@@ -31,7 +33,7 @@ public final class InboundProcessStatus {
|
||||
|
||||
public static final Set<String> VALUES = Set.of(
|
||||
WAIT_MEASURE, MEASURING, MEASURE_PENDING, MEASURE_REJECTED,
|
||||
WAIT_PRICING, FEE_CONFIRMING, INBOUNDING, INBOUNDED,
|
||||
WAIT_PRICING, FEE_CONFIRMING, INBOUNDING, INBOUNDED, WAIT_SPLIT,
|
||||
LOADING, LOADED, DELIVERED, VOIDED
|
||||
);
|
||||
|
||||
|
||||
+9
-2
@@ -10,6 +10,7 @@ import com.baiyue.firstleg.dto.InboundMeasurementResponses;
|
||||
import com.baiyue.firstleg.dto.InboundOrderDetail;
|
||||
import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse;
|
||||
import com.baiyue.firstleg.client.MeasurementImageClient;
|
||||
import com.baiyue.firstleg.domain.InboundProcessStatus;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundFeeDetailEntity;
|
||||
import com.baiyue.firstleg.repository.InboundOrderRepository;
|
||||
@@ -37,7 +38,7 @@ public class MerchantInboundBillingService {
|
||||
if (inbound == null) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "订单尚未生成入库单");
|
||||
}
|
||||
if (!"INBOUNDED".equals(inbound.getProcessStatus()) || !"SENT".equals(inbound.getFeeStatus())) {
|
||||
if (!isPublished(inbound)) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "入库费用尚未由客服审核并发布");
|
||||
}
|
||||
InboundOrderDetail platformDetail = orders.detail(tenantId, inbound);
|
||||
@@ -106,12 +107,18 @@ public class MerchantInboundBillingService {
|
||||
private InboundOrderEntity requirePublished(long tenantId, long orderId) {
|
||||
InboundOrderEntity inbound = orders.findByOrderId(tenantId, orderId);
|
||||
if (inbound == null) throw new BizException(ResultCode.NOT_FOUND, "订单尚未生成入库单");
|
||||
if (!"INBOUNDED".equals(inbound.getProcessStatus()) || !"SENT".equals(inbound.getFeeStatus())) {
|
||||
if (!isPublished(inbound)) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "入库费用尚未由客服审核并发布");
|
||||
}
|
||||
return inbound;
|
||||
}
|
||||
|
||||
private static boolean isPublished(InboundOrderEntity inbound) {
|
||||
return InboundProcessStatus.WAIT_SPLIT.equals(inbound.getProcessStatus())
|
||||
&& "APPROVED".equals(inbound.getMeasureReviewStatus())
|
||||
&& "CONFIRMED".equals(inbound.getFeeStatus());
|
||||
}
|
||||
|
||||
private String previewUrl(long orderId, long fileId) {
|
||||
return "/api/merchant/v1/first-leg/orders/" + orderId
|
||||
+ "/inbound-billing/images/" + fileId;
|
||||
|
||||
@@ -434,7 +434,7 @@ paths:
|
||||
/api/merchant/v1/first-leg/orders/{orderId}/inbound-billing:
|
||||
get:
|
||||
summary: 客户查询已完成入库结果和最新已发布费用
|
||||
description: 需要 merchant:order:view 权限;客户只读查看,不需要确认费用,未完成客服审核发布时不可见。
|
||||
description: 需要 merchant:order:view 权限;客户只读查看,不需要确认费用,仅 WAIT_SPLIT + APPROVED + CONFIRMED 的二期终态可见。
|
||||
parameters:
|
||||
- {name: orderId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
responses:
|
||||
|
||||
+2
-1
@@ -53,7 +53,8 @@ class InboundOrderControllerContractTest {
|
||||
.contains("tenantId: {type: string, pattern: '^[1-9][0-9]*$', description:")
|
||||
.contains("[WAIT_MEASURE, WAIT_REVIEW, WAIT_FEE_SEND, INBOUNDED]")
|
||||
.contains("platform:firstleg:inbound:list")
|
||||
.doesNotContain("WAIT_CUSTOMER_CONFIRM");
|
||||
.doesNotContain("WAIT_CUSTOMER_CONFIRM")
|
||||
.contains("WAIT_SPLIT");
|
||||
assertThat(inboundList).contains("#/components/schemas/InboundOrderPageEnvelope");
|
||||
assertThat(taskList).doesNotContain("InboundOrderPageEnvelope");
|
||||
}
|
||||
|
||||
+7
@@ -8,7 +8,9 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -32,6 +34,11 @@ class MerchantInboundBillingControllerContractTest {
|
||||
.isEqualTo("hasAuthority('merchant:order:view')");
|
||||
assertThat(MerchantInboundBillingController.class.getDeclaredMethods())
|
||||
.noneMatch(candidate -> candidate.getName().toLowerCase().contains("confirm"));
|
||||
|
||||
String openapi = new ClassPathResource("openapi/firstleg-v1.yaml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThat(openapi).contains("/api/merchant/v1/first-leg/orders/{orderId}/inbound-billing:")
|
||||
.contains("仅 WAIT_SPLIT + APPROVED + CONFIRMED 的二期终态可见");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -40,6 +40,18 @@ class InboundOrderQueryServiceTest {
|
||||
verify(repository).page(null, query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptCurrentPhase2TerminalStatusFilters() {
|
||||
InboundOrderQuery query = new InboundOrderQuery(null, null, "WAIT_SPLIT", null,
|
||||
"APPROVED", "CONFIRMED", null, null, null, null, null, null, null,
|
||||
null, null, null, null, 1, 200);
|
||||
PageResult<InboundOrderSummary> expected = new PageResult<>(List.of(), 1, 200, 0, 0);
|
||||
when(repository.page(10001L, query)).thenReturn(expected);
|
||||
|
||||
assertThat(service.page(10001L, query)).isSameAs(expected);
|
||||
verify(repository).page(10001L, query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepV2TabMappingWithoutCustomerConfirmation() {
|
||||
assertThat(InboundListTab.WAIT_MEASURE.processStatuses())
|
||||
|
||||
+17
-5
@@ -36,7 +36,7 @@ class MerchantInboundBillingServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldHandleCase01() {
|
||||
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
|
||||
InboundOrderEntity inbound = inbound("WAIT_SPLIT", "APPROVED", "CONFIRMED");
|
||||
InboundOrderDetail detail = new InboundOrderDetail(mock(com.baiyue.firstleg.dto.InboundOrderSummary.class),
|
||||
"客户备注", List.of(), List.of(
|
||||
new InboundOrderDetail.FileSummary(1L, "RELATED", "public.pdf", "PUBLIC", 1, 1L, null),
|
||||
@@ -83,7 +83,7 @@ class MerchantInboundBillingServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldHandleCase02() {
|
||||
InboundOrderEntity inbound = inbound("FEE_CONFIRMING", "CONFIRMED");
|
||||
InboundOrderEntity inbound = inbound("WAIT_SPLIT", "PENDING", "CONFIRMED");
|
||||
when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound);
|
||||
|
||||
assertThatThrownBy(() -> service.find(10001L, 77L))
|
||||
@@ -104,7 +104,7 @@ class MerchantInboundBillingServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldHandleCase04() {
|
||||
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
|
||||
InboundOrderEntity inbound = inbound("WAIT_SPLIT", "APPROVED", "CONFIRMED");
|
||||
InboundMeasurementResponses.Detail measurement = mock(InboundMeasurementResponses.Detail.class);
|
||||
InboundMeasurementResponses.Item item = mock(InboundMeasurementResponses.Item.class);
|
||||
when(item.intelligentImageFileId()).thenReturn(41L);
|
||||
@@ -127,7 +127,7 @@ class MerchantInboundBillingServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldHandleCase05() {
|
||||
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
|
||||
InboundOrderEntity inbound = inbound("WAIT_SPLIT", "APPROVED", "CONFIRMED");
|
||||
InboundMeasurementResponses.Detail measurement = mock(InboundMeasurementResponses.Detail.class);
|
||||
when(measurement.items()).thenReturn(List.of());
|
||||
when(measurement.weightResults()).thenReturn(List.of());
|
||||
@@ -140,10 +140,22 @@ class MerchantInboundBillingServiceTest {
|
||||
verify(imageClient, never()).download("internal-token", 10001L, 88L, 99L);
|
||||
}
|
||||
|
||||
private static InboundOrderEntity inbound(String process, String feeStatus) {
|
||||
@Test
|
||||
void shouldRejectHistoricalPublishedStatusCombination() {
|
||||
InboundOrderEntity inbound = inbound("INBOUNDED", "APPROVED", "SENT");
|
||||
when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound);
|
||||
|
||||
assertThatThrownBy(() -> service.find(10001L, 77L))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("尚未由客服审核并发布");
|
||||
verify(pricing, never()).latest(10001L, 88L);
|
||||
}
|
||||
|
||||
private static InboundOrderEntity inbound(String process, String reviewStatus, String feeStatus) {
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setId(88L);
|
||||
inbound.setProcessStatus(process);
|
||||
inbound.setMeasureReviewStatus(reviewStatus);
|
||||
inbound.setFeeStatus(feeStatus);
|
||||
return inbound;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user