diff --git a/lastleg-service/docs/procurement-direction.md b/lastleg-service/docs/procurement-direction.md index 9d1dbea0b..1066c3d71 100644 --- a/lastleg-service/docs/procurement-direction.md +++ b/lastleg-service/docs/procurement-direction.md @@ -2,7 +2,7 @@ 2026-10-26 按用户要求增加国内采购发往海外,保留海外采购回国内。 -- `procurementDirection` 为 `OVERSEAS_TO_DOMESTIC` 或 `DOMESTIC_TO_OVERSEAS`,不传时保持历史进口方向。客户建单可选;待审核且无购买、收货事实时平台可核定方向,审核通过后不再通过通用编辑修改方向。 +- `procurementDirection` 为 `OVERSEAS_TO_DOMESTIC` 或 `DOMESTIC_TO_OVERSEAS`,不传时保持历史进口方向。客户建单可选;平台审核仅展示客户下单方向,不允许审核改向;采购仓按该方向筛选。审核底部直接取消、拒绝或通过,拒绝必须填写具体原因。 - 起运收货仓按方向读取 Warehouse 权威公共启用仓目录,国内方向只接受 DOMESTIC,进口方向保持海外 RECEIVING 能力约束。实际收货、计划仓更改、实际仓更正及面单寄件地址均校验同一方向。 - 出口流程:审核 → 国内购买 → 国内分次实收并确认 → 装箱 → 国际面单、预估运费冻结、贴单 → 国内国际交寄 → 海外分次实收并确认 → 海外核定实际费用 → 寄海外客户。正常终态仍为 SHIPPED_TO_CUSTOMER,取消、拒绝、异常关闭规则保留。 - 此次沿用单商品、一次购买、两段各整批交寄及 Admin 台账,不新增 WMS 库存、PDA 入账、分批购买或分批交寄。目的地接收地址沿用国际面单地址快照,最终客户地址由下单提供,最终面单变更仍需客户凭证。 diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java index 2075a6ebd..8894c6e1c 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java @@ -234,8 +234,11 @@ public class OverseasProcurementService { if (!PENDING_REVIEW.equals(order.getStatus())) throw conflict("Overseas procurement order was already reviewed"); String direction = request.procurementDirection() == null ? ProcurementDirection.normalize(order.getProcurementDirection()) : ProcurementDirection.normalize(request.procurementDirection()); - if (!direction.equals(ProcurementDirection.normalize(order.getProcurementDirection())) && directionRepository.hasStarted(order.getId())) { - throw conflict("本单已有购买或收货事实,不能切换采购方向"); + if (!direction.equals(ProcurementDirection.normalize(order.getProcurementDirection()))) { + throw conflict("审核不能修改采购方向,请按客户下单方向选择采购仓"); + } + if (REJECTED.equals(next) && trim(request.remark()) == null) { + throw badRequest("请填写拒绝原因"); } Map before = reviewSnapshot(order); if (WAIT_PURCHASE.equals(next)) { diff --git a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml index e23c85b8b..9e3ca0c81 100644 --- a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml @@ -5055,7 +5055,7 @@ components: ProcurementReview: type: object required: [version, decision, idempotencyKey] - description: 审核通过必须选择与采购方向匹配的有效公共收货仓;国内采购选择国内仓,海外采购选择海外收货仓;拒绝不要求仓库。已有购买或收货事实不得切换方向。 + description: 审核通过必须选择与采购方向匹配的有效公共收货仓;国内采购选择国内仓,海外采购选择海外收货仓;拒绝不要求仓库,但 remark 必须填写非空白拒绝原因。审核不得修改客户下单采购方向。 properties: procurementDirection: { $ref: '#/components/schemas/ProcurementDirection' } version: { type: integer, format: int64, minimum: 0 } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/procurement/service/OverseasProcurementServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/procurement/service/OverseasProcurementServiceTest.java index 8d52bdc3d..bb02ba618 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/procurement/service/OverseasProcurementServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/procurement/service/OverseasProcurementServiceTest.java @@ -195,6 +195,7 @@ class OverseasProcurementServiceTest { @Test void exportReviewPersistsDirectionAndCannotReplayWithChangedDirection() { var order = order(OverseasProcurementService.PENDING_REVIEW,0,0); + order.setProcurementDirection(ProcurementDirection.EXPORT); when(orders.lockNo("100","HC1")).thenReturn(order); when(orders.findNo("100","HC1")).thenReturn(order); when(histories.listOrder("100",1L)).thenReturn(List.of()); @@ -221,7 +222,7 @@ class OverseasProcurementServiceTest { @Test void pendingReviewCannotReverseDirectionOfAnAlreadyRecordedPurchase() { var order = order(OverseasProcurementService.PENDING_REVIEW,0,0); when(orders.lockNo("100","HC1")).thenReturn(order); - when(directionRepository.hasStarted(1L)).thenReturn(true); + assertThrows(ResponseStatusException.class, () -> service.review("100","HC1","7", new OverseasProcurementRequests.Review(0L,"APPROVE","export-after-purchase","checked",10L,ProcurementDirection.EXPORT))); verify(warehouseDirectory,never()).required(10L,ProcurementDirection.EXPORT); @@ -239,4 +240,30 @@ class OverseasProcurementServiceTest { assertEquals("US, California, test destination",detail.receivingAddress()); assertEquals(OverseasProcurementService.PENDING_REVIEW,detail.summary().status()); } + + @Test void rejectionRequiresSpecificNonblankReasonWithoutAWarehouse() { + var order = order(OverseasProcurementService.PENDING_REVIEW,0,0); + when(orders.lockNo("100","HC1")).thenReturn(order); + for (String reason : new String[]{null,""," "}) { + var error = assertThrows(ResponseStatusException.class, () -> service.review("100","HC1","7", + new OverseasProcurementRequests.Review(0L,"REJECT","reject-empty",reason,null))); + assertEquals("请填写拒绝原因",error.getReason()); + } + assertEquals(OverseasProcurementService.PENDING_REVIEW,order.getStatus()); + when(orders.findNo("100","HC1")).thenReturn(order); + when(histories.listOrder("100",1L)).thenReturn(List.of()); + var result=service.review("100","HC1","7",new OverseasProcurementRequests.Review(0L,"REJECT","reject-reason","供应商无法供货",null)); + assertEquals(OverseasProcurementService.REJECTED,result.summary().status()); + assertEquals("供应商无法供货",result.reviewRemark()); + verify(warehouseDirectory,never()).required(10L); + } + @Test void reviewCannotChangeDirectionEvenBeforePurchase() { + var order = order(OverseasProcurementService.PENDING_REVIEW,0,0); + order.setProcurementDirection(ProcurementDirection.EXPORT); + when(orders.lockNo("100","HC1")).thenReturn(order); + var error=assertThrows(ResponseStatusException.class, () -> service.review("100","HC1","7", + new OverseasProcurementRequests.Review(0L,"APPROVE","change-direction","checked",10L,ProcurementDirection.IMPORT))); + assertEquals("审核不能修改采购方向,请按客户下单方向选择采购仓",error.getReason()); + assertEquals(ProcurementDirection.EXPORT,order.getProcurementDirection()); + } }