Files
baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/service/FeeManagementServiceTest.java
T
2026-10-19 17:56:36 +08:00

555 lines
25 KiB
Java

package com.baiyue.firstleg.service;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.FeeItemDtos;
import com.baiyue.firstleg.dto.InboundFeeDetailDtos;
import com.baiyue.firstleg.entity.FeeItemEntity;
import com.baiyue.firstleg.entity.InboundFeeDetailEntity;
import com.baiyue.firstleg.entity.InboundFeeEntity;
import com.baiyue.firstleg.entity.InboundFeeItemEntity;
import com.baiyue.firstleg.entity.InboundOrderEntity;
import com.baiyue.firstleg.repository.FeeItemRepository;
import com.baiyue.firstleg.repository.FbaPricingRepository;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.ArgumentCaptor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.lenient;
@DisplayName("费用项目和入库费用明细")
@SpringJUnitConfig({FeeItemService.class, InboundFeeDetailService.class})
class FeeManagementServiceTest {
@MockBean
private FeeItemRepository repository;
@MockBean
private FbaPricingRepository pricingRepository;
@MockBean
private FinanceFeeSourceOutboxService financeOutbox;
@MockBean
private WarehouseSupplierSnapshotService suppliers;
@MockBean
private FeePaymentSnapshotValidator paymentSnapshots;
@Resource
private FeeItemService feeItems;
@Resource
private InboundFeeDetailService details;
@BeforeEach
void paymentSnapshot() {
lenient().when(paymentSnapshots.validate(anyLong(), any(), any(), any(), any(), any(), any()))
.thenAnswer(invocation -> new FeePaymentSnapshotValidator.Snapshot(
invocation.getArgument(2), invocation.getArgument(3), invocation.getArgument(4),
invocation.getArgument(5), invocation.getArgument(6) == null ? List.of() : invocation.getArgument(6)));
}
@Test
@DisplayName("客户B不能删除客户A的费用明细")
void rejectsCrossCustomerFeeDetailDelete() {
assertThatThrownBy(() -> details.delete(10002L, 21L, 31L))
.isInstanceOf(BizException.class)
.hasMessageContaining("费用明细不存在");
verify(repository).findDetail(10002L, 21L, 31L);
verify(repository, never()).deleteDetail(10002L, 21L, 31L);
}
@Test
@DisplayName("待审核费用明细按客户和入库单作用域删除")
void deletesPendingFeeDetailInInboundScope() {
InboundFeeDetailEntity pending = new InboundFeeDetailEntity();
pending.setReviewStatus("PENDING");
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(pending);
when(repository.deleteDetail(10001L, 21L, 31L)).thenReturn(1);
details.delete(10001L, 21L, 31L);
verify(repository).deleteDetail(10001L, 21L, 31L);
}
@Test
@DisplayName("费用项目列表返回分页结果")
void pagesItems() {
PageResult<FeeItemEntity> expected = new PageResult<>(List.of(), 2, 20, 21, 2);
when(repository.listItems("仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
.thenReturn(expected);
assertThat(feeItems.list("仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
.isSameAs(expected);
}
@Test
@DisplayName("新增费用项目保存原型字段")
void createsItem() {
doAnswer(invocation -> {
((FeeItemEntity) invocation.getArgument(0)).setId(11L);
return null;
}).when(repository).insertItem(any());
FeeItemEntity created = feeItems.create(9L, "admin", itemRequest());
assertThat(created.getId()).isEqualTo(11L);
assertThat(created.getAllocationFee()).isTrue();
assertThat(created.getDefaultPrice()).isEqualByComparingTo("60.00");
assertThat(created.getWarehouseReceivable()).isTrue();
assertThat(created.getWarehousePayable()).isTrue();
assertThat(created.getSupplierCategory()).isNull();
assertThat(created.getFeeCode()).startsWith("MANUAL_");
assertThat(created.getTenantId()).isZero();
assertThat(created.getCreatedByName()).isEqualTo("admin");
}
@Test
@DisplayName("自定义费用编码统一转为大写并检查客户内唯一性")
void normalizesCustomFeeCode() {
FeeItemDtos.SaveRequest request = new FeeItemDtos.SaveRequest("仓储成本", "INTERNATIONAL",
"storage_fee", null, "仓库租赁费", false, false, true, true, true, "应付仓库租赁费", null);
FeeItemEntity created = feeItems.create(9L, "admin", request);
verify(repository).itemCodeExists("STORAGE_FEE", null);
assertThat(created.getFeeCode()).isEqualTo("STORAGE_FEE");
}
@Test
@DisplayName("并发创建相同费用编码返回业务冲突")
void rejectsConcurrentDuplicateFeeCode() {
FeeItemDtos.SaveRequest request = new FeeItemDtos.SaveRequest("仓储成本", "INTERNATIONAL",
"STORAGE_FEE", null, "仓库租赁费", false, false, true, true, true, null, null);
doThrow(new DuplicateKeyException("duplicate fee code")).when(repository).insertItem(any());
assertThatThrownBy(() -> feeItems.create(9L, "admin", request))
.isInstanceOf(BizException.class)
.hasMessageContaining("费用项目编码已存在");
}
@Test
@DisplayName("编辑时省略费用编码保留原稳定编码")
void preservesFeeCodeWhenUpdateOmitsIt() {
FeeItemEntity existing = item(true, true);
existing.setFeeCode("STORAGE_FEE");
when(repository.findItem(11L)).thenReturn(existing);
when(repository.updateItem(any())).thenReturn(1);
FeeItemEntity updated = feeItems.update(11L, 9L, "admin", itemRequest());
assertThat(updated.getFeeCode()).isEqualTo("STORAGE_FEE");
verify(repository, never()).itemCodeExists("STORAGE_FEE", 11L);
}
/** 清空默认价只修改公共项目,不触碰已生成的费用。 */
@Test
void clearsDefaultPrice() {
FeeItemEntity existing = item(true, true);
existing.setDefaultPrice(new BigDecimal("60.00"));
when(repository.findItem(11L)).thenReturn(existing);
when(repository.updateItem(any())).thenReturn(1);
FeeItemDtos.SaveRequest request = new FeeItemDtos.SaveRequest("仓储成本", "INTERNATIONAL",
null, null, "仓库租赁费", false, false, true, true, true, null, null);
assertThat(feeItems.update(11L, 9L, "admin", request).getDefaultPrice()).isNull();
verify(repository).updateItem(existing);
}
/** 请求价格允许空值和零元,拒绝负数、过多小数和超出字段范围的金额。 */
@Test
void validatesDefaultPrice() {
try (var factory = jakarta.validation.Validation.buildDefaultValidatorFactory()) {
var validator = factory.getValidator();
for (String price : List.of("0", "60.25", "9999999999.99")) {
var request = new FeeItemDtos.SaveRequest("仓储成本", "DOMESTIC", null, null,
"操作费", false, true, false, false, false, null, new BigDecimal(price));
assertThat(validator.validate(request)).isEmpty();
}
for (String price : List.of("-1", "60.255", "10000000000")) {
var request = new FeeItemDtos.SaveRequest("仓储成本", "DOMESTIC", null, null,
"操作费", false, true, false, false, false, null, new BigDecimal(price));
assertThat(validator.validate(request)).isNotEmpty();
}
}
}
@Test
@DisplayName("任一客户引用公共费用项目后都禁止删除")
void rejectsDeletingItemBoundByAnyCustomer() {
when(repository.findItem(11L)).thenReturn(item(true, true));
when(repository.countBindings(11L)).thenReturn(1L);
assertThatThrownBy(() -> feeItems.delete(11L))
.isInstanceOf(BizException.class)
.hasMessageContaining("已被入库费用明细使用");
verify(repository, never()).deleteItem(11L);
}
@Test
@DisplayName("入库费用绑定项目并生成订单快照")
void bindsInboundFee() {
InboundOrderEntity inbound = inbound();
FeeItemEntity item = item(true, true);
when(repository.findInbound(10001L, 21L)).thenReturn(inbound);
when(repository.findItem(11L)).thenReturn(item);
doAnswer(invocation -> {
((InboundFeeDetailEntity) invocation.getArgument(0)).setId(31L);
return null;
}).when(repository).insertDetail(any());
InboundFeeDetailEntity created = details.create(10001L, 21L, 9L,
detailRequest("RECEIVABLE"));
assertThat(created.getId()).isEqualTo(31L);
assertThat(created.getInboundCode()).isEqualTo("RK-20260805-001");
assertThat(created.getFeeTypeNameSnapshot()).isEqualTo("仓库租赁费");
assertThat(created.getActualGrossWeightKg()).isEqualByComparingTo("10.5000");
assertThat(created.getReviewStatus()).isEqualTo("PENDING");
}
@Test
@DisplayName("发布计费快照生成客户与后台共用的入库运费明细")
void publishesSharedFreightDetail() {
InboundOrderEntity inbound = inbound();
inbound.setCustomerId(101L);
inbound.setCustomerName("客户A");
inbound.setCompanyName("客户A有限公司");
when(repository.findInbound(10001L, 21L)).thenReturn(inbound);
when(repository.findBySourceFee(10001L, "FBA_PRICING", 501L)).thenReturn(null);
FeeItemEntity freightItem = item(true, false);
freightItem.setId(11L);
freightItem.setFeeCode("FBA_HEAD_FREIGHT");
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(freightItem);
InboundFeeEntity fee = new InboundFeeEntity();
fee.setId(501L);
fee.setInboundId(21L);
fee.setFeeNo("FBAF-21-1");
fee.setFeeVersion(1);
fee.setFeeStatus("SENT");
fee.setActualWeightKg(new BigDecimal("100.0000"));
fee.setActualVolumeCbm(new BigDecimal("1.000000"));
fee.setChargeableQuantity(new BigDecimal("167.0000"));
fee.setChargeUnit("KG");
fee.setCurrency("USD");
fee.setTotalAmount(new BigDecimal("354.00"));
when(pricingRepository.findFee(10001L, 501L)).thenReturn(fee);
InboundFeeItemEntity base = new InboundFeeItemEntity();
base.setUnitPrice(new BigDecimal("2.0000"));
when(pricingRepository.findBaseItem(10001L, 501L)).thenReturn(base);
LocalDateTime publishedAt = LocalDateTime.of(2026, 8, 5, 12, 0);
InboundFeeDetailEntity result = details.publishFreight(
10001L, 21L, 501L, 9L, "admin", publishedAt);
ArgumentCaptor<InboundFeeDetailEntity> captor = ArgumentCaptor.forClass(InboundFeeDetailEntity.class);
verify(repository).insertDetail(captor.capture());
InboundFeeDetailEntity saved = captor.getValue();
assertThat(result).isSameAs(saved);
assertThat(saved.getFeeItemId()).isEqualTo(11L);
assertThat(saved.getSourceFeeId()).isEqualTo(501L);
assertThat(saved.getDirection()).isEqualTo("RECEIVABLE");
assertThat(saved.getAmount()).isEqualByComparingTo("354.00");
assertThat(saved.getUnitPrice()).isEqualByComparingTo("2.1198");
assertThat(saved.getReviewStatus()).isEqualTo("APPROVED");
assertThat(saved.getPublicationStatus()).isEqualTo("PUBLISHED");
}
@Test
@DisplayName("查询费用明细会幂等补齐历史初算缺失的派生行")
void materializesMissingLatestCalculatedFreightOnList() {
InboundOrderEntity inbound = inbound();
inbound.setCustomerId(101L);
inbound.setCustomerName("客户A");
when(repository.findInbound(10001L, 21L)).thenReturn(inbound);
when(repository.findBySourceFee(10001L, "FBA_PRICING", 501L)).thenReturn(null);
when(repository.listDetails(10001L, 21L, false)).thenReturn(List.of());
FeeItemEntity freightItem = item(true, false);
freightItem.setFeeCode("FBA_HEAD_FREIGHT");
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(freightItem);
InboundFeeEntity fee = new InboundFeeEntity();
fee.setId(501L); fee.setInboundId(21L); fee.setFeeNo("FBAF-21-2");
fee.setFeeVersion(2); fee.setFeeStatus("CALCULATED");
fee.setActualWeightKg(new BigDecimal("100.0000"));
fee.setActualVolumeCbm(new BigDecimal("1.000000"));
fee.setChargeableQuantity(new BigDecimal("167.0000"));
fee.setChargeUnit("KG"); fee.setCurrency("USD");
fee.setTotalAmount(new BigDecimal("354.00"));
fee.setCalculatedAt(LocalDateTime.of(2026, 8, 17, 12, 0));
when(pricingRepository.findLatestFee(10001L, 21L)).thenReturn(fee);
when(pricingRepository.findFee(10001L, 501L)).thenReturn(fee);
details.list(10001L, 21L, false);
verify(repository).insertDetail(any(InboundFeeDetailEntity.class));
verify(repository).listDetails(10001L, 21L, false);
}
@Test
@DisplayName("系统头程运费项目缺失时拒绝运行期补建")
void rejectsMissingSystemFreightItem() {
when(repository.findInbound(10001L, 21L)).thenReturn(inbound());
when(repository.findBySourceFee(10001L, "FBA_PRICING", 501L)).thenReturn(null);
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(null);
InboundFeeEntity fee = new InboundFeeEntity();
fee.setId(501L);
fee.setInboundId(21L);
fee.setFeeStatus("SENT");
when(pricingRepository.findFee(10001L, 501L)).thenReturn(fee);
assertThatThrownBy(() -> details.publishFreight(
10001L, 21L, 501L, 9L, "admin", LocalDateTime.of(2026, 8, 5, 12, 0)))
.isInstanceOf(BizException.class)
.hasMessageContaining("FBA头程运费未初始化");
verify(repository, never()).insertItem(any());
}
@Test
@DisplayName("并发发布同一计费快照时返回已生成的运费明细")
void reusesConcurrentFreightDetail() {
InboundFeeDetailEntity concurrent = new InboundFeeDetailEntity();
concurrent.setId(99L);
when(repository.findBySourceFee(10001L, "FBA_PRICING", 501L))
.thenReturn(null, concurrent);
when(repository.findInbound(10001L, 21L)).thenReturn(inbound());
FeeItemEntity freightItem = item(true, false);
freightItem.setFeeCode("FBA_HEAD_FREIGHT");
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(freightItem);
InboundFeeEntity fee = new InboundFeeEntity();
fee.setId(501L);
fee.setInboundId(21L);
fee.setFeeNo("FBAF-21-1");
fee.setFeeVersion(1);
fee.setFeeStatus("SENT");
fee.setActualWeightKg(new BigDecimal("100.0000"));
fee.setActualVolumeCbm(new BigDecimal("1.000000"));
fee.setChargeableQuantity(new BigDecimal("1.0000"));
fee.setChargeUnit("CBM");
fee.setCurrency("USD");
fee.setTotalAmount(new BigDecimal("200.00"));
when(pricingRepository.findFee(10001L, 501L)).thenReturn(fee);
doThrow(new DuplicateKeyException("duplicate source fee"))
.when(repository).insertDetail(any());
InboundFeeDetailEntity result = details.publishFreight(
10001L, 21L, 501L, 9L, "admin", LocalDateTime.of(2026, 8, 5, 12, 0));
assertThat(result).isSameAs(concurrent);
}
@Test
@DisplayName("拒绝绑定未启用仓储应收的项目")
void rejectsInapplicableItem() {
when(repository.findInbound(10001L, 21L)).thenReturn(inbound());
when(repository.findItem(11L)).thenReturn(item(false, true));
assertThatThrownBy(() -> details.create(10001L, 21L, 9L,
detailRequest("RECEIVABLE")))
.isInstanceOf(BizException.class)
.hasMessageContaining("仓储应收/应付");
}
@Test
@DisplayName("私人仓尾程派送费只允许PRIVATE目的地")
void restrictsPrivateLastMileDeliveryFeeByDestinationType() {
InboundOrderEntity fbaInbound = inbound();
fbaInbound.setDestinationType("FBA");
FeeItemEntity privateDelivery = item(true, true);
privateDelivery.setFeeCode("PRIVATE_LAST_MILE_DELIVERY");
when(repository.findInbound(10001L, 21L)).thenReturn(fbaInbound);
when(repository.findItem(11L)).thenReturn(privateDelivery);
assertThatThrownBy(() -> details.create(10001L, 21L, 9L,
detailRequest("RECEIVABLE")))
.isInstanceOf(BizException.class)
.hasMessageContaining("目的地类型为PRIVATE");
verify(repository, never()).insertDetail(any());
fbaInbound.setDestinationType("PRIVATE");
doAnswer(invocation -> { ((InboundFeeDetailEntity) invocation.getArgument(0)).setId(32L); return null; })
.when(repository).insertDetail(any());
assertThat(details.create(10001L, 21L, 9L, detailRequest("RECEIVABLE")).getId())
.isEqualTo(32L);
}
@Test
@DisplayName("审核只更新待审核明细")
void approvesPendingDetail() {
InboundFeeDetailEntity pending = new InboundFeeDetailEntity();
pending.setId(31L);
pending.setTenantId(10001L);
pending.setInboundId(21L);
pending.setFeeItemId(11L);
pending.setDirection("RECEIVABLE");
pending.setSourceType("MANUAL");
pending.setSourceFeeVersion(1);
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(pending);
when(repository.findItem(11L)).thenReturn(item(true, true));
when(repository.updateDetail(pending)).thenReturn(1);
when(repository.review(10001L, 21L, 31L, "APPROVED", 9L, "admin", null)).thenReturn(1);
details.approve(10001L, 21L, 31L, 9L, "admin");
verify(repository).review(10001L, 21L, 31L, "APPROVED", 9L, "admin", null);
verify(financeOutbox).publishInbound(eq(FinanceFeeSourceOutboxService.PUBLISHED),
eq(10001L), eq(21L), eq(31L), eq(1), any(LocalDateTime.class));
}
@Test
@DisplayName("审核通过的人工费用自动写入Finance事件")
void publishesApprovedSettlementFee() {
InboundFeeDetailEntity approved = new InboundFeeDetailEntity();
approved.setId(31L);
approved.setTenantId(10001L);
approved.setInboundId(21L);
approved.setFeeItemId(11L);
approved.setDirection("RECEIVABLE");
approved.setSourceType("MANUAL");
approved.setSourceFeeVersion(1);
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(approved);
when(repository.findItem(11L)).thenReturn(item(true, true));
when(repository.updateDetail(approved)).thenReturn(1);
when(repository.review(10001L, 21L, 31L, "APPROVED", 9L, "admin", null)).thenReturn(1);
details.approve(10001L, 21L, 31L, 9L, "admin");
verify(financeOutbox).publishInbound(eq(FinanceFeeSourceOutboxService.PUBLISHED),
eq(10001L), eq(21L), eq(31L), eq(1), any(LocalDateTime.class));
}
@Test
@DisplayName("自动头程运费派生明细不能在费用明细中二次审核")
void rejectsReviewingDerivedFreight() {
InboundFeeDetailEntity derived = new InboundFeeDetailEntity();
derived.setSourceType("FBA_PRICING");
derived.setReviewStatus("PENDING");
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(derived);
assertThatThrownBy(() -> details.approve(10001L, 21L, 31L, 9L, "admin"))
.isInstanceOf(BizException.class)
.hasMessageContaining("只读派生");
verify(repository, never()).review(10001L, 21L, 31L, "APPROVED", 9L, "admin", null);
}
@Test
@DisplayName("已审核人工费用调整保留旧版本并创建待审核新版本")
void versionsApprovedManualAdjustment() {
InboundFeeDetailEntity approved = new InboundFeeDetailEntity();
approved.setId(31L);
approved.setTenantId(10001L);
approved.setInboundId(21L);
approved.setSourceType("MANUAL");
approved.setSourceFeeVersion(1);
approved.setReviewStatus("APPROVED");
approved.setPublicationStatus("PUBLISHED");
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(approved);
when(repository.findInbound(10001L, 21L)).thenReturn(inbound());
when(repository.findItem(11L)).thenReturn(item(true, true));
when(repository.supersedeApprovedManual(eq(10001L), eq(21L), eq(31L),
eq(1), eq(9L), any(LocalDateTime.class))).thenReturn(1);
doAnswer(invocation -> {
((InboundFeeDetailEntity) invocation.getArgument(0)).setId(32L);
return null;
}).when(repository).insertDetail(any());
InboundFeeDetailEntity next = details.adjust(
10001L, 21L, 31L, 1, 9L, detailRequest("RECEIVABLE"));
assertThat(next.getId()).isEqualTo(32L);
assertThat(next.getSourceFeeId()).isEqualTo(31L);
assertThat(next.getSourceFeeVersion()).isEqualTo(2);
assertThat(next.getReviewStatus()).isEqualTo("PENDING");
assertThat(next.getPublicationStatus()).isEqualTo("DRAFT");
verify(repository).insertDetail(next);
}
@Test
@DisplayName("作废已入池人工费用引用当前版本并发布VOIDED")
void voidsPublishedSettlementFee() {
InboundFeeDetailEntity current = new InboundFeeDetailEntity();
current.setId(31L);
current.setTenantId(10001L);
current.setInboundId(21L);
current.setFeeItemId(11L);
current.setSourceType("MANUAL");
current.setSourceFeeVersion(1);
InboundFeeDetailEntity voided = new InboundFeeDetailEntity();
voided.setId(31L);
voided.setTenantId(10001L);
voided.setInboundId(21L);
voided.setFeeItemId(11L);
voided.setSourceType("MANUAL");
voided.setSourceFeeVersion(1);
when(repository.findDetail(10001L, 21L, 31L)).thenReturn(current, voided);
when(repository.voidApproved(10001L, 21L, 31L, 1, 9L)).thenReturn(1);
details.voidApproved(10001L, 21L, 31L, 1, 9L);
verify(financeOutbox).publishInbound(eq(FinanceFeeSourceOutboxService.VOIDED),
eq(10001L), eq(21L), eq(31L), eq(1), any(LocalDateTime.class));
}
/** 构造带默认价格的公共费用项目。 */
private static FeeItemDtos.SaveRequest itemRequest() {
return new FeeItemDtos.SaveRequest("仓储成本", "INTERNATIONAL", null, null, "仓库租赁费",
false, false, true, true, true, "应付仓库租赁费", new BigDecimal("60.00"));
}
private static InboundFeeDetailDtos.SaveRequest detailRequest(String direction) {
return new InboundFeeDetailDtos.SaveRequest(11L, direction, LocalDate.of(2026, 8, 5),
new BigDecimal("12.3456"), "CNY", new BigDecimal("123.46"), "联调费用");
}
private static FeeItemEntity item(boolean receivable, boolean payable) {
FeeItemEntity item = new FeeItemEntity();
item.setId(11L);
item.setCategory("仓储成本");
item.setTradeScope("INTERNATIONAL");
item.setFeeTypeName("仓库租赁费");
item.setWarehouseReceivable(receivable);
item.setWarehousePayable(payable);
item.setStatus("ACTIVE");
return item;
}
private static InboundOrderEntity inbound() {
InboundOrderEntity inbound = new InboundOrderEntity();
inbound.setId(21L);
inbound.setInboundCode("RK-20260805-001");
inbound.setChannelCode("SEA");
inbound.setChannelName("海运");
inbound.setDestinationCode("ONT8");
inbound.setDestinationName("ONT8");
inbound.setTotalPackageCount(10);
inbound.setActualGrossWeightKg(new BigDecimal("10.5000"));
inbound.setActualNetWeightKg(new BigDecimal("10.5000"));
inbound.setActualVolumeCbm(new BigDecimal("1.250000"));
inbound.setVolumeWeightKg(new BigDecimal("208.3333"));
inbound.setChargeableWeightKg(new BigDecimal("208.3333"));
inbound.setChargeableVolumeCbm(new BigDecimal("1.250000"));
return inbound;
}
}