费用提交
This commit is contained in:
+49
@@ -43,6 +43,7 @@ class OverseasProcurementServiceTest {
|
||||
@Mock OverseasProcurementOrderMapper orders;
|
||||
@Mock OverseasProcurementStatusHistoryMapper histories;
|
||||
@Mock ProcurementWarehouseDirectory warehouseDirectory;
|
||||
@Mock com.baiyue.lastleg.procurement.repository.ProcurementDirectionRepository directionRepository;
|
||||
@Mock com.baiyue.lastleg.service.LastlegDocumentNumberGenerator orderNoGenerator;
|
||||
@Mock OverseasProcurementFileMapper procurementFiles;
|
||||
@Mock OverseasProcurementFeeMapper procurementFees;
|
||||
@@ -57,6 +58,7 @@ class OverseasProcurementServiceTest {
|
||||
ReflectionTestUtils.setField(service, "orders", orders);
|
||||
ReflectionTestUtils.setField(service, "histories", histories);
|
||||
ReflectionTestUtils.setField(service, "warehouseDirectory", warehouseDirectory);
|
||||
ReflectionTestUtils.setField(service, "directionRepository", directionRepository);
|
||||
ReflectionTestUtils.setField(service, "objectMapper", new ObjectMapper());
|
||||
ReflectionTestUtils.setField(service, "orderNoGenerator", orderNoGenerator);
|
||||
ReflectionTestUtils.setField(service, "procurementFiles", procurementFiles);
|
||||
@@ -190,4 +192,51 @@ class OverseasProcurementServiceTest {
|
||||
assertEquals("label.pdf",service.downloadFile("100","HC1",42).fileName());
|
||||
verify(fileGateway).download("100",1,"LABEL",42);
|
||||
}
|
||||
|
||||
@Test void exportReviewPersistsDirectionAndCannotReplayWithChangedDirection() {
|
||||
var order = order(OverseasProcurementService.PENDING_REVIEW,0,0);
|
||||
when(orders.lockNo("100","HC1")).thenReturn(order);
|
||||
when(orders.findNo("100","HC1")).thenReturn(order);
|
||||
when(histories.listOrder("100",1L)).thenReturn(List.of());
|
||||
when(warehouseDirectory.required(10L,ProcurementDirection.EXPORT)).thenReturn(
|
||||
new com.baiyue.lastleg.client.DomesticWarehouseDirectoryClient.ReceivingWarehouse(10L,"SZX","国内采购仓","CN","ACTIVE"));
|
||||
var request = new OverseasProcurementRequests.Review(0L,"APPROVE","export-review","已核对海外收件资料",10L,ProcurementDirection.EXPORT);
|
||||
var result = service.review("100","HC1","7",request);
|
||||
assertEquals(ProcurementDirection.EXPORT,result.summary().procurementDirection());
|
||||
assertEquals("国内采购仓",result.summary().purchaseWarehouseName());
|
||||
assertEquals(1L,service.review("100","HC1","7",request).summary().version());
|
||||
assertThrows(ResponseStatusException.class, () -> service.review("100","HC1","7",
|
||||
new OverseasProcurementRequests.Review(0L,"APPROVE","export-review","已核对海外收件资料",10L,ProcurementDirection.IMPORT)));
|
||||
verify(warehouseDirectory,never()).required(10L);
|
||||
}
|
||||
|
||||
@Test void oldRequestHashDoesNotGainNullDirectionField() throws Exception {
|
||||
var request = new OverseasProcurementRequests.Review(0L,"APPROVE","old-key","checked",10L);
|
||||
var json = new ObjectMapper().writeValueAsString(request);
|
||||
assertEquals(false,json.contains("procurementDirection"));
|
||||
assertEquals(ProcurementDirection.IMPORT,ProcurementDirection.normalize(null));
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
assertEquals(OverseasProcurementService.PENDING_REVIEW,order.getStatus());
|
||||
}
|
||||
|
||||
@Test void clientCanCreateDomesticPurchaseForAnOverseasRecipient() {
|
||||
AtomicReference<OverseasProcurementOrder> saved = new AtomicReference<>();
|
||||
when(orders.insert(any(OverseasProcurementOrder.class))).thenAnswer(invocation -> { saved.set(invocation.getArgument(0)); return 1; });
|
||||
when(orders.findNo(anyString(),anyString())).thenAnswer(ignored -> saved.get());
|
||||
when(histories.listOrder(anyString(),any())).thenReturn(List.of());
|
||||
var detail=service.create("100","7",new OverseasProcurementRequests.Create("Acme","Office","Sample","A",null,10L,
|
||||
BigDecimal.TEN,"CNY","US, California, test destination","Buyer","+1 1234567890",null,"export-create",ProcurementDirection.EXPORT));
|
||||
assertEquals(ProcurementDirection.EXPORT,detail.summary().procurementDirection());
|
||||
assertEquals("US, California, test destination",detail.receivingAddress());
|
||||
assertEquals(OverseasProcurementService.PENDING_REVIEW,detail.summary().status());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -81,9 +81,8 @@ class ProcurementFinancePersistenceTest {
|
||||
var saved=fees.selectById(fee.getId());assertThat(saved.getWalletStatus()).isEqualTo("SETTLED");
|
||||
assertThat(saved.getSourceFeeId()).isEqualTo(901L);
|
||||
verify(sources).publishSettled(eq("7"),anyString(),any(),any(),argThat(ref -> ref.commandEventId().equals(settled.commandEventId())));
|
||||
assertThat(service.sourceLinked(saved)).isFalse();
|
||||
jdbc.update("INSERT INTO ll_event_outbox VALUES('OVERSEAS_PROCUREMENT_FEE','901','PUBLISHED')");
|
||||
assertThat(service.sourceLinked(saved)).isTrue();
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_event_outbox", Integer.class)).isZero();
|
||||
}
|
||||
|
||||
@Test void fullInboundNeverSettlesEstimateAndActualUsesOriginalHold() {
|
||||
@@ -205,10 +204,8 @@ class ProcurementFinancePersistenceTest {
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_procurement_wallet_attempt WHERE fee_id=?",Integer.class,fee.getId())).isEqualTo(2);
|
||||
verify(sources,times(2)).publishSettled(anyString(),anyString(),any(),any(),argThat(ref->ref.commandEventId().equals(result.commandEventId())));
|
||||
verify(commands,times(2)).publish(anyString(),anyLong(),anyString(),anyString(),any(),anyString(),anyLong());
|
||||
assertThat(service.sourceLinked(recovered)).isFalse();
|
||||
assertThatThrownBy(()->service.requireShipmentReady(11)).hasMessageContaining("来源关联");
|
||||
jdbc.update("INSERT INTO ll_event_outbox VALUES('OVERSEAS_PROCUREMENT_FEE','901','PUBLISHED')");
|
||||
assertThat(service.sourceLinked(recovered)).isTrue();
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_event_outbox", Integer.class)).isZero();
|
||||
service.requireShipmentReady(11);
|
||||
}
|
||||
|
||||
@@ -260,8 +257,7 @@ class ProcurementFinancePersistenceTest {
|
||||
assertThatThrownBy(()->service.requireShipmentReady(11)).hasMessageContaining("未决资金");
|
||||
handle(result(fee,"FREEZE","SUCCEEDED","FROZEN","100"));
|
||||
handle(result(fees.selectById(fee.getId()),"SETTLE","SUCCEEDED","SETTLED","100"));
|
||||
assertThatThrownBy(()->service.requireShipmentReady(11)).hasMessageContaining("来源关联");
|
||||
jdbc.update("INSERT INTO ll_event_outbox VALUES('OVERSEAS_PROCUREMENT_FEE','901','PUBLISHED')");
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_event_outbox", Integer.class)).isZero();
|
||||
service.requireShipmentReady(11);
|
||||
tx.execute(s->service.refund("9","PO-1","7",fee.getId(),new BigDecimal("20"),1,"correction","pending-refund"));
|
||||
assertThatThrownBy(()->service.requireShipmentReady(11)).hasMessageContaining("未决资金");
|
||||
|
||||
+9
@@ -65,4 +65,13 @@ class ProcurementQueryServiceTest {
|
||||
fixture.jdbc.update("INSERT INTO ll_procurement_carton VALUES(201,11,301,'OLD',20,30,40,5,10),(202,11,302,'DRAFT',20,30,40,5,10)");
|
||||
assertThat(query.customer("9","PO-1").boxes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test void exportProjectionExposesDirectionAndLabelsWithoutRewritingHistoricalLegs() {
|
||||
fixture.jdbc.update("UPDATE ll_overseas_procurement_order SET procurement_direction=? WHERE id=11",ProcurementDirection.EXPORT);
|
||||
var result=query.platform("9","PO-1",Set.of("platform:overseas-business:purchase:receive-overseas"));
|
||||
assertThat(result.customerView().order().procurementDirection()).isEqualTo(ProcurementDirection.EXPORT);
|
||||
assertThat(result.customerView().progress()).extracting(ProcurementQueryService.Progress::title).contains("国内收货完成","海外收货完成","海外费用核定");
|
||||
assertThat(result.actions()).filteredOn(a -> a.key().equals("RECEIVE_OVERSEAS")).singleElement().satisfies(a -> assertThat(a.label()).isEqualTo("登记国内实收"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-1
@@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.*;
|
||||
|
||||
class ProcurementTransportPersistenceTest {
|
||||
JdbcTemplate jdbc;TransactionTemplate tx;OverseasProcurementOrderMapper orders;
|
||||
ProcurementWarehouseDirectory warehouses;
|
||||
ProcurementTransportService service;ProcurementFinanceService finance;OverseasProcurementFee estimate;
|
||||
final Address warehouse=new Address("Overseas","1234567890","US operating address");
|
||||
final Address domestic=new Address("Domestic","13800000000","Shanghai receiving address");
|
||||
@@ -60,7 +61,7 @@ class ProcurementTransportPersistenceTest {
|
||||
jdbc.update("INSERT INTO ll_procurement_carton VALUES(201,11,301,'C1',20,30,40,6.125,6),(202,11,301,'C2',20,30,40,4.125,4)");
|
||||
jdbc.update("INSERT INTO ll_procurement_carton_source VALUES(201,101,6),(202,101,4)");
|
||||
jdbc.update("INSERT INTO ll_overseas_procurement_file(id,order_id,customer_id,category,file_id,file_name,content_type,size_bytes,created_by) VALUES(501,11,'9','LABEL',1001,'labels.pdf','application/pdf',10,'7'),(502,11,'9','EXCEPTION',1002,'proof.pdf','application/pdf',10,'7')");
|
||||
var warehouses=mock(ProcurementWarehouseDirectory.class);when(warehouses.sender(91)).thenReturn(warehouse);
|
||||
warehouses=mock(ProcurementWarehouseDirectory.class);when(warehouses.sender(91)).thenReturn(warehouse);
|
||||
finance=mock(ProcurementFinanceService.class);var payment=new OverseasProcurementFee();payment.setWalletStatus("SETTLED");
|
||||
when(finance.current(11,ProcurementFeePurpose.SAMPLE_PAYMENT,"SAMPLE_PAYMENT")).thenReturn(payment);when(finance.sourceLinked(payment)).thenReturn(true);
|
||||
estimate=new OverseasProcurementFee();estimate.setId(801L);estimate.setAmount(BigDecimal.ZERO);estimate.setWalletStatus("ZERO");
|
||||
@@ -172,4 +173,32 @@ class ProcurementTransportPersistenceTest {
|
||||
private void apply(){tx.execute(s->service.applyLabels("9","PO-1","7",new ApplyLabels(version(),"apply-"+version(),labelVersion(Leg.OVERSEAS),List.of(201L,202L),start.plusMinutes(30),"attached")));}
|
||||
private Ship shipRequest(Leg leg,String key){return new Ship(version(),key,leg,labelVersion(leg),List.of(201L,202L),start.plusMinutes(leg==Leg.OVERSEAS?60:100),"actual handoff");}
|
||||
private ProcurementTransportService.Snapshot ship(Leg leg,String key){return tx.execute(s->service.ship("9","PO-1","7",shipRequest(leg,key)));}
|
||||
|
||||
@Test void exportUsesDomesticSenderAndOverseasDestinationThenEndsAfterLocalHandoff() {
|
||||
var sender = new Address("Domestic warehouse","13800000000","Shenzhen domestic origin");
|
||||
var receiver = new Address("Overseas warehouse","+1 1234567890","Los Angeles overseas receiving");
|
||||
var finalReceiver = new Address("Overseas customer","+1 9876543210","San Diego customer destination");
|
||||
jdbc.update("UPDATE ll_overseas_procurement_order SET procurement_direction=?,contact_name=?,contact_phone=?,receiving_address=? WHERE id=11",
|
||||
ProcurementDirection.EXPORT,finalReceiver.name(),finalReceiver.phone(),finalReceiver.address());
|
||||
when(warehouses.sender(91,ProcurementDirection.EXPORT)).thenReturn(sender);
|
||||
tx.execute(s -> service.saveLabels("9","PO-1","7",new Labels(version(),"export-labels",Leg.OVERSEAS,1,receiver,null,List.of(label(201),label(202)),"export manifest")));
|
||||
review(BigDecimal.ZERO); apply();
|
||||
assertThat(ship(Leg.OVERSEAS,"export-international").status()).isEqualTo("INTERNATIONAL_TRANSIT");
|
||||
String snapshot = jdbc.queryForObject("SELECT address_snapshot FROM ll_procurement_shipment WHERE leg='OVERSEAS'",String.class);
|
||||
assertThat(snapshot).contains(sender.address(),receiver.address()).doesNotContain(warehouse.address());
|
||||
jdbc.update("INSERT INTO ll_procurement_receipt(id,order_id,customer_id,receipt_no,leg,source_carton_id,good_quantity,damaged_quantity,extra_quantity,received_at,created_by) VALUES(102,11,'9','PR-2','DOMESTIC',201,6,0,0,?,'7'),(103,11,'9','PR-3','DOMESTIC',202,4,0,0,?,'7')",start.plusMinutes(65),start.plusMinutes(65));
|
||||
jdbc.update("UPDATE ll_procurement_flow SET domestic_received_at=? WHERE order_id=11",start.plusMinutes(70));
|
||||
tx.execute(s -> service.saveLabels("9","PO-1","7",new Labels(version(),"export-local-labels",Leg.DOMESTIC,1,finalReceiver,null,List.of(label(201),label(202)),"overseas delivery")));
|
||||
assertThatThrownBy(() -> ship(Leg.DOMESTIC,"before-charges")).hasMessageContaining("费用尚未确认");
|
||||
jdbc.update("UPDATE ll_procurement_flow SET charges_confirmed_at=? WHERE order_id=11",start.plusMinutes(80));
|
||||
var request = shipRequest(Leg.DOMESTIC,"export-local");
|
||||
assertThat(tx.execute(s -> service.ship("9","PO-1","7",request)).status()).isEqualTo("SHIPPED_TO_CUSTOMER");
|
||||
tx.execute(s -> service.ship("9","PO-1","7",request));
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_procurement_shipment",Integer.class)).isEqualTo(2);
|
||||
String local = jdbc.queryForObject("SELECT address_snapshot FROM ll_procurement_shipment WHERE leg='DOMESTIC'",String.class);
|
||||
assertThat(local).contains(receiver.address(),finalReceiver.address());
|
||||
verify(warehouses,never()).sender(91);
|
||||
verify(finance).requireShipmentReady(11);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
@@ -32,4 +32,25 @@ class ProcurementWarehouseDirectoryTest {
|
||||
receiving();when(client.activeWarehouse(91,"test-internal")).thenReturn(ApiResponse.success(new Detail(new Warehouse(92L,9L,"LAX","Other","OVERSEAS","US","ACTIVE","Receiver","1234567890"),List.of(address))));
|
||||
assertThatThrownBy(()->directory.sender(91)).hasMessageContaining("暂不可用");
|
||||
}
|
||||
|
||||
@Test void domesticDirectionUsesDomesticCatalogAndChecksMasterScopeTypeAndStatus() {
|
||||
ReflectionTestUtils.setField(directory,"token","test-internal");
|
||||
when(client.domesticWarehouses("test-internal")).thenReturn(ApiResponse.success(List.of(new DomesticWarehouse(91L,"SZX","深圳仓"))));
|
||||
assertThat(directory.options(ProcurementDirection.EXPORT)).singleElement().satisfies(w -> assertThat(w.warehouseId()).isEqualTo(91L));
|
||||
var domesticAddress = new Address("OPERATING","CN","广东","深圳",null,"518000","测试国内作业地址",null);
|
||||
when(client.activeWarehouse(91,"test-internal")).thenReturn(ApiResponse.success(new Detail(
|
||||
new Warehouse(91L,0L,"SZX","深圳仓","DOMESTIC","CN","ACTIVE","仓管","13800000000"),List.of(domesticAddress))));
|
||||
assertThat(directory.sender(91,ProcurementDirection.EXPORT).address()).contains("深圳","测试国内作业地址");
|
||||
verify(client,never()).receivingWarehouse(anyLong(),anyString());
|
||||
for (var invalid : List.of(
|
||||
new Warehouse(91L,0L,"LAX","海外仓","OVERSEAS","US","ACTIVE","仓管","13800000000"),
|
||||
new Warehouse(91L,9L,"SZX","客户仓","DOMESTIC","CN","ACTIVE","仓管","13800000000"),
|
||||
new Warehouse(91L,0L,"SZX","停用仓","DOMESTIC","CN","INACTIVE","仓管","13800000000"),
|
||||
new Warehouse(92L,0L,"SZX","其他仓","DOMESTIC","CN","ACTIVE","仓管","13800000000"))) {
|
||||
when(client.activeWarehouse(91,"test-internal")).thenReturn(ApiResponse.success(new Detail(invalid,List.of(domesticAddress))));
|
||||
assertThatThrownBy(() -> directory.required(91,ProcurementDirection.EXPORT)).hasMessageContaining("国内公共仓");
|
||||
}
|
||||
assertThatThrownBy(() -> directory.options("INVALID")).hasMessageContaining("方向无效");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user