优化
This commit is contained in:
@@ -2,10 +2,15 @@ package com.baiyue.firstleg.controller;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -34,6 +39,53 @@ class FeeManagementContractTest {
|
||||
assertPermission("delete", "firstleg:fee-item:delete");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("平台费用项目接口不接受客户作用域")
|
||||
void platformFeeItemEndpointsDoNotExposeAccountId() {
|
||||
Arrays.stream(PlatformFeeItemController.class.getDeclaredMethods())
|
||||
.filter(method -> List.of("list", "detail", "create", "update", "changeStatus", "delete")
|
||||
.contains(method.getName()))
|
||||
.flatMap(method -> Arrays.stream(method.getParameters()))
|
||||
.forEach(parameter -> assertThat(parameter.getName()).isNotEqualTo("accountId"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("费用项目OpenAPI包含完整响应和空响应契约")
|
||||
void openApiIsComplete() throws Exception {
|
||||
String source = new ClassPathResource("openapi/fee-item-v1.yaml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
Map<String, Object> document = stringMap(new Yaml().load(source));
|
||||
assertThat(document.get("openapi")).isEqualTo("3.0.3");
|
||||
assertThat(source).doesNotContain("accountId", "AccountId");
|
||||
|
||||
Map<String, Object> paths = stringMap(document.get("paths"));
|
||||
assertThat(paths).containsOnlyKeys(
|
||||
"/api/platform/v1/fee-items",
|
||||
"/api/platform/v1/fee-items/{id}",
|
||||
"/api/platform/v1/fee-items/{id}/status");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items", "get", "FeeItemPageApiResponse");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items", "post", "FeeItemApiResponse");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "get", "FeeItemApiResponse");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "put", "FeeItemApiResponse");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "delete", "VoidApiResponse");
|
||||
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}/status", "patch", "VoidApiResponse");
|
||||
|
||||
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
||||
assertThat(schemas).containsKeys("FeeItemSaveRequest", "FeeItemResource", "FeeItemPage",
|
||||
"FeeItemApiResponse", "FeeItemPageApiResponse", "VoidApiResponse");
|
||||
for (String response : List.of("FeeItemApiResponse", "FeeItemPageApiResponse", "VoidApiResponse")) {
|
||||
assertThat(list(stringMap(schemas.get(response)).get("required"))).contains("timestamp");
|
||||
assertThat(properties(schemas, response)).containsKeys(
|
||||
"code", "message", "data", "requestId", "traceId", "timestamp");
|
||||
}
|
||||
assertThat(properties(schemas, "VoidApiResponse").get("data"))
|
||||
.isInstanceOf(Map.class)
|
||||
.satisfies(value -> assertThat(stringMap(value)).containsEntry("nullable", true));
|
||||
assertThat(stringMap(properties(schemas, "FeeItemSaveRequest").get("feeCode")))
|
||||
.containsEntry("maxLength", 50)
|
||||
.containsEntry("pattern", "^[A-Za-z0-9_-]+$");
|
||||
}
|
||||
|
||||
private static void assertPermission(String methodName, String permission) {
|
||||
Method method = Arrays.stream(PlatformFeeItemController.class.getDeclaredMethods())
|
||||
.filter(candidate -> candidate.getName().equals(methodName))
|
||||
@@ -41,4 +93,29 @@ class FeeManagementContractTest {
|
||||
assertThat(method.getAnnotation(PreAuthorize.class).value())
|
||||
.isEqualTo("hasAuthority('" + permission + "')");
|
||||
}
|
||||
|
||||
private static void assertResponseSchema(Map<String, Object> paths, String path,
|
||||
String operation, String schema) {
|
||||
Map<String, Object> endpoint = stringMap(paths.get(path));
|
||||
Map<String, Object> response = stringMap(stringMap(endpoint.get(operation)).get("responses"));
|
||||
Map<String, Object> success = stringMap(response.get("200"));
|
||||
Map<String, Object> content = stringMap(success.get("content"));
|
||||
Map<String, Object> mediaType = stringMap(content.get("application/json"));
|
||||
assertThat(stringMap(mediaType.get("schema")))
|
||||
.containsEntry("$ref", "#/components/schemas/" + schema);
|
||||
}
|
||||
|
||||
private static Map<String, Object> properties(Map<String, Object> schemas, String schema) {
|
||||
return stringMap(stringMap(schemas.get(schema)).get("properties"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> stringMap(Object value) {
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<String> list(Object value) {
|
||||
return (List<String>) value;
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -4,6 +4,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -25,4 +27,27 @@ class LoadingEvidenceSqlContractTest {
|
||||
.contains("idx_loading_evidence_seal (tenant_id, seal_no, status)")
|
||||
.contains("fl_inbound_loading_pick_task");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unifiedEntryMigratesLegacySingleSlotEvidenceWithoutRecreatingUniqueIndexes() throws Exception {
|
||||
Path direct = Path.of("deploy", "sql", "头程数据库统一增量-MySQL8.sql");
|
||||
Path unified = Files.exists(direct) ? direct : Path.of("..", "deploy", "sql", "头程数据库统一增量-MySQL8.sql");
|
||||
String sql = Files.readString(unified, StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(sql)
|
||||
.contains("file_id BIGINT UNSIGNED NOT NULL")
|
||||
.contains("status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE'")
|
||||
.contains("phase3_loading_evidence_add_column('status', 'VARCHAR(16) NOT NULL DEFAULT ''ACTIVE'' COMMENT ''ACTIVE/VOID''')")
|
||||
.contains("phase3_loading_evidence_add_column('void_reason'")
|
||||
.contains("phase3_loading_evidence_add_column('voided_by'")
|
||||
.contains("phase3_loading_evidence_add_column('voided_by_name'")
|
||||
.contains("phase3_loading_evidence_add_column('voided_at'")
|
||||
.contains("DROP INDEX uk_loading_evidence_type")
|
||||
.contains("DROP INDEX uk_loading_evidence_seal")
|
||||
.contains("ADD KEY idx_loading_evidence_type (tenant_id,inbound_id,evidence_type,status)")
|
||||
.contains("ADD KEY idx_loading_evidence_seal (tenant_id,seal_no,status)")
|
||||
.doesNotContain("phase3_loading_evidence_add_column('status', \"VARCHAR(16)")
|
||||
.doesNotContain("UNIQUE KEY uk_loading_evidence_type")
|
||||
.doesNotContain("UNIQUE KEY uk_loading_evidence_seal");
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -4,6 +4,7 @@ import com.baiyue.firstleg.mapper.InboundSplitSettlementMapper;
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
|
||||
import org.apache.ibatis.annotations.Insert;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
@@ -76,4 +77,16 @@ class PdaSplitSettlementSqlContractTest {
|
||||
.contains("#{childOrderId},#{childOrderNo}")
|
||||
.contains("CASE WHEN #{splitPurpose}='LOADED' THEN 'LOADING' ELSE 'INBOUNDED' END");
|
||||
}
|
||||
|
||||
@Test
|
||||
void readsSplitStatusAndAllowsPdaSettlementAfterPreloadLoading() throws Exception {
|
||||
String lockParent = InboundSplitSettlementMapper.class.getMethod("lockParent", long.class)
|
||||
.getAnnotation(Select.class).value()[0];
|
||||
assertThat(lockParent).contains("split_status AS splitStatus");
|
||||
|
||||
String markRoot = InboundSplitSettlementMapper.class.getMethod("markRootSplit",
|
||||
long.class, long.class, long.class, int.class)
|
||||
.getAnnotation(Update.class).value()[0];
|
||||
assertThat(markRoot).contains("process_status IN ('INBOUNDED','LOADING')");
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -27,7 +27,12 @@ class Phase3FinalUnifiedSqlContractTest {
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_assignment")
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_review")
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_operation")
|
||||
.contains("uk_loading_evidence_seal (tenant_id,seal_no)")
|
||||
.contains("phase3_loading_evidence_add_column")
|
||||
.contains("DROP INDEX uk_loading_evidence_type")
|
||||
.contains("DROP INDEX uk_loading_evidence_seal")
|
||||
.contains("ADD KEY idx_loading_evidence_type (tenant_id,inbound_id,evidence_type,status)")
|
||||
.contains("ADD KEY idx_loading_evidence_seal (tenant_id,seal_no,status)")
|
||||
.contains("column_name IN ('status','void_reason','voided_by','voided_by_name','voided_at')")
|
||||
.contains("CALL assert_phase3_final_schema()")
|
||||
.contains("trg_port_schedule_history_before_update")
|
||||
.contains("trg_port_schedule_history_before_delete")
|
||||
|
||||
+25
@@ -59,6 +59,7 @@ class TenantScopedMutationRepositoryTest {
|
||||
initialize(FulfillmentTaskEntity.class);
|
||||
initialize(InboundWeightResultEntity.class);
|
||||
initialize(InboundOrderEntity.class);
|
||||
initialize(FeeItemEntity.class);
|
||||
initialize(InboundFeeDetailEntity.class);
|
||||
}
|
||||
|
||||
@@ -122,6 +123,30 @@ class TenantScopedMutationRepositoryTest {
|
||||
assertTenantAndId(wrapper.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
void feeItemLookupIsAlwaysPlatformScoped() {
|
||||
feeRepository.findItem(ID);
|
||||
|
||||
ArgumentCaptor<Wrapper<FeeItemEntity>> wrapper = ArgumentCaptor.forClass(Wrapper.class);
|
||||
verify(feeItems).selectOne(wrapper.capture());
|
||||
assertThat(wrapper.getValue().getCustomSqlSegment()).contains("tenant_id");
|
||||
assertThat(((AbstractWrapper<?, ?, ?>) wrapper.getValue()).getParamNameValuePairs().values())
|
||||
.contains(0L, ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
void feeItemBindingCountChecksAllCustomers() {
|
||||
feeRepository.countBindings(ID);
|
||||
|
||||
ArgumentCaptor<Wrapper<InboundFeeDetailEntity>> wrapper = ArgumentCaptor.forClass(Wrapper.class);
|
||||
verify(feeDetails).selectCount(wrapper.capture());
|
||||
assertThat(wrapper.getValue().getCustomSqlSegment()).doesNotContain("tenant_id");
|
||||
assertThat(((AbstractWrapper<?, ?, ?>) wrapper.getValue()).getParamNameValuePairs().values())
|
||||
.contains(ID);
|
||||
}
|
||||
|
||||
private static void initialize(Class<?> entityType) {
|
||||
TableInfoHelper.initTableInfo(
|
||||
new MapperBuilderAssistant(new MybatisConfiguration(), "test"), entityType);
|
||||
|
||||
@@ -60,10 +60,10 @@ class FeeManagementServiceTest {
|
||||
@DisplayName("费用项目列表返回分页结果")
|
||||
void pagesItems() {
|
||||
PageResult<FeeItemEntity> expected = new PageResult<>(List.of(), 2, 20, 21, 2);
|
||||
when(repository.listItems(10001L, "仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
|
||||
when(repository.listItems("仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
|
||||
.thenReturn(expected);
|
||||
|
||||
assertThat(feeItems.list(10001L, "仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
|
||||
assertThat(feeItems.list("仓储成本", "INTERNATIONAL", "人工", "ACTIVE", 2, 20))
|
||||
.isSameAs(expected);
|
||||
}
|
||||
|
||||
@@ -75,21 +75,74 @@ class FeeManagementServiceTest {
|
||||
return null;
|
||||
}).when(repository).insertItem(any());
|
||||
|
||||
FeeItemEntity created = feeItems.create(10001L, 9L, "admin", itemRequest());
|
||||
FeeItemEntity created = feeItems.create(9L, "admin", itemRequest());
|
||||
|
||||
assertThat(created.getId()).isEqualTo(11L);
|
||||
assertThat(created.getAllocationFee()).isTrue();
|
||||
assertThat(created.getWarehouseReceivable()).isTrue();
|
||||
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", "仓库租赁费", false, false, true, true, true, "应付仓库租赁费");
|
||||
|
||||
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", "仓库租赁费", false, false, true, true, true, 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
|
||||
@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(10001L, 11L)).thenReturn(item);
|
||||
when(repository.findItem(11L)).thenReturn(item);
|
||||
doAnswer(invocation -> {
|
||||
((InboundFeeDetailEntity) invocation.getArgument(0)).setId(31L);
|
||||
return null;
|
||||
@@ -114,9 +167,10 @@ class FeeManagementServiceTest {
|
||||
inbound.setCompanyName("客户A有限公司");
|
||||
when(repository.findInbound(10001L, 21L)).thenReturn(inbound);
|
||||
when(repository.findBySourceFee(10001L, "FBA_PRICING", 501L)).thenReturn(null);
|
||||
when(repository.findItemByCode(10001L, "FBA_HEAD_FREIGHT")).thenReturn(null);
|
||||
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(null);
|
||||
doAnswer(invocation -> {
|
||||
FeeItemEntity value = invocation.getArgument(0);
|
||||
assertThat(value.getTenantId()).isZero();
|
||||
value.setId(11L);
|
||||
return null;
|
||||
}).when(repository).insertItem(any());
|
||||
@@ -167,7 +221,7 @@ class FeeManagementServiceTest {
|
||||
|
||||
FeeItemEntity freightItem = item(true, false);
|
||||
freightItem.setFeeCode("FBA_HEAD_FREIGHT");
|
||||
when(repository.findItemByCode(10001L, "FBA_HEAD_FREIGHT")).thenReturn(freightItem);
|
||||
when(repository.findItemByCode("FBA_HEAD_FREIGHT")).thenReturn(freightItem);
|
||||
|
||||
InboundFeeEntity fee = new InboundFeeEntity();
|
||||
fee.setId(501L);
|
||||
@@ -195,7 +249,7 @@ class FeeManagementServiceTest {
|
||||
@DisplayName("拒绝绑定未启用仓储应收的项目")
|
||||
void rejectsInapplicableItem() {
|
||||
when(repository.findInbound(10001L, 21L)).thenReturn(inbound());
|
||||
when(repository.findItem(10001L, 11L)).thenReturn(item(false, true));
|
||||
when(repository.findItem(11L)).thenReturn(item(false, true));
|
||||
|
||||
assertThatThrownBy(() -> details.create(10001L, 21L, 9L,
|
||||
detailRequest("RECEIVABLE")))
|
||||
|
||||
+45
-1
@@ -152,6 +152,45 @@ class PdaPickSettlementServiceTest {
|
||||
verify(splits).removeSkippedPlanItem(22L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesTotalPackageCountWhenUnsplitMeasuredInboundHasZeroAvailableColumn() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 20, 20, "PICKED");
|
||||
resolved(List.of(item));
|
||||
when(splits.lockParent(31L)).thenReturn(parent(20, 0, "INBOUNDED", "NOT_SPLIT"));
|
||||
when(splits.lockLatestApprovedItemsForInbound(10001L, 31L))
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L))
|
||||
.thenReturn(List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(
|
||||
List.of(released(91L, 501L), released(92L, 502L)), List.of())));
|
||||
writes(0, 20);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
verify(splits).cloneChildWithPurpose(eq(10001L), eq(31L), eq(31L), anyLong(),
|
||||
eq(1001L), eq("ORDER-LOADED"), anyString(), eq(20), anyInt(), eq("LOADED"), eq(9L), eq("operator"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsPdaSettlementAfterPreloadMovesMasterToLoading() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 20, 20, "PICKED");
|
||||
resolved(List.of(item));
|
||||
when(splits.lockParent(31L)).thenReturn(parent(20, 20, "LOADING", "NOT_SPLIT"));
|
||||
when(splits.lockLatestApprovedItemsForInbound(10001L, 31L))
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L))
|
||||
.thenReturn(List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(
|
||||
List.of(released(91L, 501L), released(92L, 502L)), List.of())));
|
||||
writes(0, 20);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
verify(splits).markRootSplit(10001L, 31L, 9L, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mixedSkippedInboundIsNotRestored() {
|
||||
LoadingPickItemEntity picked = item(11L, 21L, 31L, 41L, 8, 8, "PICKED");
|
||||
@@ -250,7 +289,12 @@ class PdaPickSettlementServiceTest {
|
||||
}
|
||||
|
||||
private static ParentRow parent(int packageCount) {
|
||||
return new ParentRow(31L, 10001L, "IN-001", "MASTER", "INBOUNDED", packageCount, packageCount, 3);
|
||||
return parent(packageCount, packageCount, "INBOUNDED", "NOT_SPLIT");
|
||||
}
|
||||
|
||||
private static ParentRow parent(int totalPackageCount, int packageCount, String processStatus, String splitStatus) {
|
||||
return new ParentRow(31L, 10001L, 901L, "ORDER-001", "IN-001", "MASTER", 31L,
|
||||
processStatus, totalPackageCount, packageCount, splitStatus, 3);
|
||||
}
|
||||
|
||||
private static InboundPalletAllocationEntity allocation(long id, int no, long locationId, int allocated) {
|
||||
|
||||
Reference in New Issue
Block a user