fix(dispatch): publish capacity demands only during creation
This commit is contained in:
@@ -417,7 +417,7 @@ CREATE TABLE ll_dispatch_order (
|
|||||||
id BIGINT NOT NULL COMMENT '整车排车主单内部主键',
|
id BIGINT NOT NULL COMMENT '整车排车主单内部主键',
|
||||||
dispatch_no VARCHAR(64) NOT NULL COMMENT '真实排车业务号',
|
dispatch_no VARCHAR(64) NOT NULL COMMENT '真实排车业务号',
|
||||||
task_type VARCHAR(32) NOT NULL DEFAULT 'DELIVERY' COMMENT '任务类型:仓库派送、到仓取货、本土卡车',
|
task_type VARCHAR(32) NOT NULL DEFAULT 'DELIVERY' COMMENT '任务类型:仓库派送、到仓取货、本土卡车',
|
||||||
publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否向内部散板需求池发布余位',
|
publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否向内部散板需求池发布余位',
|
||||||
trip_id BIGINT NOT NULL COMMENT '本趟唯一车辆资源记录ID',
|
trip_id BIGINT NOT NULL COMMENT '本趟唯一车辆资源记录ID',
|
||||||
overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '真实起运海外仓ID',
|
overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '真实起运海外仓ID',
|
||||||
overseas_warehouse_code VARCHAR(128) NULL COMMENT '起运海外仓编码快照',
|
overseas_warehouse_code VARCHAR(128) NULL COMMENT '起运海外仓编码快照',
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
| 财务费用来源快照 | `controller/InternalFinanceFeeSourceController.java`;入库费用与柜次费用分别按来源 ID、版本向 Finance 提供不可变快照 |
|
| 财务费用来源快照 | `controller/InternalFinanceFeeSourceController.java`;入库费用与柜次费用分别按来源 ID、版本向 Finance 提供不可变快照 |
|
||||||
| 柜次费用明细与跨客户分摊组 | `controller/PlatformContainerFeeDetailController.java`、`service/ContainerFeeDetailService.java`、`dto/ContainerFeeDetailDtos.java`;跨客户公共柜费使用 `allocation-groups` 按柜内真实客户和订单原子分摊,入仓 ID/入仓码同时填写或同时留空;各行金额合计必须等于原始柜费,审核、驳回、调整、作废均整组处理,单行入口拒绝分摊组;契约 `src/main/resources/openapi/container-fees-v1.yaml`;Admin 消费端见 `../../apps/admin-web/src/modules/loading-lists/api/containerFees.ts` |
|
| 柜次费用明细与跨客户分摊组 | `controller/PlatformContainerFeeDetailController.java`、`service/ContainerFeeDetailService.java`、`dto/ContainerFeeDetailDtos.java`;跨客户公共柜费使用 `allocation-groups` 按柜内真实客户和订单原子分摊,入仓 ID/入仓码同时填写或同时留空;各行金额合计必须等于原始柜费,审核、驳回、调整、作废均整组处理,单行入口拒绝分摊组;契约 `src/main/resources/openapi/container-fees-v1.yaml`;Admin 消费端见 `../../apps/admin-web/src/modules/loading-lists/api/containerFees.ts` |
|
||||||
| 财务整柜成本分摊快照 | `loading/controller/InternalFinanceCostSnapshotController.java`、`loading/service/FinanceCostSnapshotService.java` |
|
| 财务整柜成本分摊快照 | `loading/controller/InternalFinanceCostSnapshotController.java`、`loading/service/FinanceCostSnapshotService.java` |
|
||||||
| 分柜、预装、真实柜号后补与正式装柜清单生命周期 | `split/controller/PlatformSplitPreloadController.java`、`controller/PlatformLoadingListController.java`、`service/LoadingListService.java`;契约 `src/main/resources/openapi/loading-lists-v1.yaml`,详情阶段先查 `/loading-plans/{planId}/lifecycle`。创建柜次时原子生成唯一预装计划,加入入仓码明细时同步创建入库预装聚合与关联,不设推送步骤;同一 `planId` 在 `EDITING/LOADING` 返回 `PRELOAD`,`COMPLETED` 返回 `LOADING_LIST`。订单协作备注以 Order 为权威,`OrderRemarksSyncService` 负责订单列表与全部未冻结预装明细双向同步,完成装柜后保留冻结快照。订舱关联、预装确认和 PDA task 创建不依赖真实集装箱号;可在 `EDITING/LOADING` 首次补录,最终 PDA 完成前必须存在;已有值纠正仅限 `EDITING` 且任务未领取、无物理作业事实 |
|
| 分柜、预装、真实柜号后补与正式装柜清单生命周期 | `split/controller/PlatformSplitPreloadController.java`、`controller/PlatformLoadingListController.java`、`service/LoadingListService.java`;契约 `src/main/resources/openapi/loading-lists-v1.yaml`,详情阶段先查 `/loading-plans/{planId}/lifecycle`。创建柜次时原子生成唯一预装计划,加入入仓码明细时同步创建入库预装聚合与关联,不设推送步骤;同一 `planId` 在 `EDITING/LOADING` 返回 `PRELOAD`,`COMPLETED` 返回 `LOADING_LIST`。订单协作备注以 Order 为权威,`OrderRemarksSyncService` 负责订单列表与全部未冻结预装明细双向同步,完成装柜后保留冻结快照。订舱关联允许真实集装箱号暂缺;确认预装和创建 PDA task 前必须补齐非空柜号,不按 ISO 格式阻断;可在 `EDITING/LOADING` 首次补录,PDA 完成前再次校验;已有值纠正仅限 `EDITING` 且任务未领取、无物理作业事实 |
|
||||||
| 同柜不可拆组 | `loading/coload/controller/PlatformColoadGroupController.java`、`service/ColoadGroupService.java`、`persistence/ColoadGroupMapper.java`;契约 `src/main/resources/openapi/coload-groups-v1.yaml`。请求按订单 ID 建组,服务端允许待审核、待测量/测量中、待分柜及装柜中订单,冻结每票全部有效入仓片段与满箱数;成员全部未进柜时创建未锁定组,成员已满箱进入同一装柜计划时直接创建锁定组,部分占用或跨柜时拒绝;仅要求同国内仓、同渠道,允许跨客户及不同目的仓港;分配、移除、取消均保持整组原子 |
|
| 同柜不可拆组 | `loading/coload/controller/PlatformColoadGroupController.java`、`service/ColoadGroupService.java`、`persistence/ColoadGroupMapper.java`;契约 `src/main/resources/openapi/coload-groups-v1.yaml`。请求按订单 ID 建组,服务端允许待审核、待测量/测量中、待分柜及装柜中订单,冻结每票全部有效入仓片段与满箱数;成员全部未进柜时创建未锁定组,成员已满箱进入同一装柜计划时直接创建锁定组,部分占用或跨柜时拒绝;仅要求同国内仓、同渠道,允许跨客户及不同目的仓港;分配、移除、取消均保持整组原子 |
|
||||||
| 货柜与全局智能排柜 | `controller/PlatformContainerController.java`、`PlatformSmartLoadingController.java`、`service/SmartLoadingService.java`、`repository/SmartLoadingRepository.java`、`client/PendingReviewOrderClient.java`、`loading/`;全局候选合并本服务履约投影与 Order `/internal/order/v1/pending-review-orders` 权威待审核页,工作台柜次从 `/inbound-candidates`、`/containers` 进入;普通箱数、待审核占位、同柜组和全部未拣移除分别走 `/inbound-assignments`、`/pending-review-assignments`、`/group-assignments`、`/unpicked-items`,明细规格拆分留在服务端 |
|
| 货柜与全局智能排柜 | `controller/PlatformContainerController.java`、`PlatformSmartLoadingController.java`、`service/SmartLoadingService.java`、`repository/SmartLoadingRepository.java`、`client/PendingReviewOrderClient.java`、`loading/`;全局候选合并本服务履约投影与 Order `/internal/order/v1/pending-review-orders` 权威待审核页,工作台柜次从 `/inbound-candidates`、`/containers` 进入;普通箱数、待审核占位、同柜组和全部未拣移除分别走 `/inbound-assignments`、`/pending-review-assignments`、`/group-assignments`、`/unpicked-items`,明细规格拆分留在服务端 |
|
||||||
| 货柜资料、PDA 证据与业务总览 | `controller/PlatformContainerController.java` 的 `/{id}/overview`、`/{id}/documents/{documentType}`,聚合 `mapper/ContainerOverviewMapper.java`;契约 `src/main/resources/openapi/phase3-container-overview-v1.yaml`,四类资料固定为目的港清关、国内出口报关、DO、PU 的当前唯一版本 |
|
| 货柜资料、PDA 证据与业务总览 | `controller/PlatformContainerController.java` 的 `/{id}/overview`、`/{id}/documents/{documentType}`,聚合 `mapper/ContainerOverviewMapper.java`;契约 `src/main/resources/openapi/phase3-container-overview-v1.yaml`,四类资料固定为目的港清关、国内出口报关、DO、PU 的当前唯一版本 |
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
| 贴标申请、箱唛/SKU/组合换标与 PDA 完工 | `relabel/` |
|
| 贴标申请、箱唛/SKU/组合换标与 PDA 完工 | `relabel/` |
|
||||||
| PDA 仓内通用扫码与操作历史 | `pda/warehouse/controller/PdaWarehouseController.java` |
|
| PDA 仓内通用扫码与操作历史 | `pda/warehouse/controller/PdaWarehouseController.java` |
|
||||||
| 船节点接收与海外 PDA 可收货门禁 | `shipping/service/ContainerShippingNodeConsumer.java` |
|
| 船节点接收与海外 PDA 可收货门禁 | `shipping/service/ContainerShippingNodeConsumer.java` |
|
||||||
| 内部散板需求只读来源 | `dispatch/service/DispatchCapacityDemandService.java`、`DispatchQueryController.java`的`/dispatches/capacity-demands`;默认发布字段随原排车创建/编辑保存,当前业务口径见拼车主契约,不恢复旧车次人工发布入口 |
|
| 内部散板需求只读来源 | `dispatch/service/DispatchCapacityDemandService.java`、`DispatchQueryController.java`的`/dispatches/capacity-demands`;发布字段仅随智能排车创建保存且缺省不发布,编辑命令不接收该字段;重发须按原门禁取消排车再重新创建,当前业务口径见拼车主契约,不恢复旧车次人工发布入口 |
|
||||||
| 平台排车查询、整车四块详情、审核与实载 | `dispatch/controller/DispatchQueryController.java`、`DispatchAggregateController.java`;公开契约`dispatch/port/`,业务规则见`../../docs/海外仓业务/排车列表统一作业改造契约.md` |
|
| 平台排车查询、整车四块详情、审核与实载 | `dispatch/controller/DispatchQueryController.java`、`DispatchAggregateController.java`;公开契约`dispatch/port/`,业务规则见`../../docs/海外仓业务/排车列表统一作业改造契约.md` |
|
||||||
| 草稿、双端装车发运 | `dispatch/controller/SmartDispatchController.java`、`dispatch/pda/controller/DispatchPdaController.java`;平台维护`DispatchController.java`;自动发运编排`dispatch/service/DispatchLoadingDepartureService.java` |
|
| 草稿、双端装车发运 | `dispatch/controller/SmartDispatchController.java`、`dispatch/pda/controller/DispatchPdaController.java`;平台维护`DispatchController.java`;自动发运编排`dispatch/service/DispatchLoadingDepartureService.java` |
|
||||||
| 仓储资源调用 | `inboundplan/pda/gateway/WarehouseResourceGateway.java` |
|
| 仓储资源调用 | `inboundplan/pda/gateway/WarehouseResourceGateway.java` |
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ public class DispatchAggregate {
|
|||||||
private String dispatchNo;
|
private String dispatchNo;
|
||||||
private String taskType;
|
private String taskType;
|
||||||
/** 是否向内部散板需求池发布本车余位。 */
|
/** 是否向内部散板需求池发布本车余位。 */
|
||||||
private Boolean publishLooseboardDemand = true;
|
private Boolean publishLooseboardDemand = false;
|
||||||
private Long tripId;
|
private Long tripId;
|
||||||
private String overseasWarehouseId;
|
private String overseasWarehouseId;
|
||||||
private String overseasWarehouseCode;
|
private String overseasWarehouseCode;
|
||||||
|
|||||||
+4
-4
@@ -24,15 +24,15 @@ public final class DispatchCommands {
|
|||||||
public record Create(String idempotencyKey, String warehouseId, Vehicle vehicle, Driver driver,
|
public record Create(String idempotencyKey, String warehouseId, Vehicle vehicle, Driver driver,
|
||||||
Instant plannedDepartureAt, Instant estimatedArrivalAt, String remark,
|
Instant plannedDepartureAt, Instant estimatedArrivalAt, String remark,
|
||||||
List<CargoSelection> cargo, List<BatchAllocation> batchAllocations, Boolean publishLooseboardDemand) {
|
List<CargoSelection> cargo, List<BatchAllocation> batchAllocations, Boolean publishLooseboardDemand) {
|
||||||
/** 新建排车默认发布内部余位,显式取消时保持不发布。 */
|
/** 仅智能排车建单时显式选择发布,未选择时不发布。 */
|
||||||
public Create { publishLooseboardDemand = publishLooseboardDemand == null ? true : publishLooseboardDemand; }
|
public Create { publishLooseboardDemand = Boolean.TRUE.equals(publishLooseboardDemand); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Update(Long recordVersion, String idempotencyKey, Vehicle vehicle, Driver driver,
|
public record Update(Long recordVersion, String idempotencyKey, Vehicle vehicle, Driver driver,
|
||||||
Instant plannedDepartureAt, Instant estimatedArrivalAt,
|
Instant plannedDepartureAt, Instant estimatedArrivalAt,
|
||||||
String remark, Boolean publishLooseboardDemand, Maintenance maintenance) {
|
String remark, Maintenance maintenance) {
|
||||||
/** 缺版本与有效版本零必须区分。 */
|
/** 缺版本与有效版本零必须区分。 */
|
||||||
public Update { recordVersion = requireVersion(recordVersion); if (publishLooseboardDemand == null) throw new IllegalArgumentException("请明确是否发布散板需求"); }
|
public Update { recordVersion = requireVersion(recordVersion); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public record AddCargo(Long recordVersion, String idempotencyKey, List<CargoSelection> cargo,
|
public record AddCargo(Long recordVersion, String idempotencyKey, List<CargoSelection> cargo,
|
||||||
|
|||||||
+1
-1
@@ -451,7 +451,7 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
|
|||||||
}
|
}
|
||||||
row.setResourcesJson(write(savedResources));row.setVehiclePlate(actual.vehicle()==null?null:actual.vehicle().plate());
|
row.setResourcesJson(write(savedResources));row.setVehiclePlate(actual.vehicle()==null?null:actual.vehicle().plate());
|
||||||
row.setPlannedDepartureAt(utc(request.plannedDepartureAt()));row.setEstimatedArrivalAt(utc(request.estimatedArrivalAt()));row.setRemark(request.remark());
|
row.setPlannedDepartureAt(utc(request.plannedDepartureAt()));row.setEstimatedArrivalAt(utc(request.estimatedArrivalAt()));row.setRemark(request.remark());
|
||||||
row.setPublishLooseboardDemand(request.publishLooseboardDemand());
|
// 发布选择仅在建单时确定,维护车辆不得取消或重新发布需求。
|
||||||
if(!Objects.equals(before,basis(row)))invalidate(row,"车辆、司机或计划信息已修改");
|
if(!Objects.equals(before,basis(row)))invalidate(row,"车辆、司机或计划信息已修改");
|
||||||
fleet.saveDispatchResources(number,actor.operatorId(),actual,request.plannedDepartureAt(),request.remark());
|
fleet.saveDispatchResources(number,actor.operatorId(),actual,request.plannedDepartureAt(),request.remark());
|
||||||
return saveCommand(row,actor,identity,request.remark());
|
return saveCommand(row,actor,identity,request.remark());
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
-- 本次仅新增排车内部余位发布选项;执行需单独授权,不回填、清理或重置业务数据。
|
-- 本次仅新增排车内部余位发布选项;执行需单独授权,不回填、清理或重置业务数据。
|
||||||
ALTER TABLE ll_dispatch_order
|
ALTER TABLE ll_dispatch_order
|
||||||
ADD COLUMN publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否向内部散板需求池发布余位' AFTER task_type;
|
ADD COLUMN publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否向内部散板需求池发布余位' AFTER task_type;
|
||||||
|
|||||||
@@ -1895,7 +1895,7 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_order (
|
|||||||
id BIGINT NOT NULL COMMENT '整车排车主单内部主键',
|
id BIGINT NOT NULL COMMENT '整车排车主单内部主键',
|
||||||
dispatch_no VARCHAR(64) NOT NULL COMMENT '真实排车业务号',
|
dispatch_no VARCHAR(64) NOT NULL COMMENT '真实排车业务号',
|
||||||
task_type VARCHAR(32) NOT NULL DEFAULT 'DELIVERY' COMMENT '任务类型:仓库派送、到仓取货、本土卡车',
|
task_type VARCHAR(32) NOT NULL DEFAULT 'DELIVERY' COMMENT '任务类型:仓库派送、到仓取货、本土卡车',
|
||||||
publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否向内部散板需求池发布余位',
|
publish_looseboard_demand TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否向内部散板需求池发布余位',
|
||||||
trip_id BIGINT NOT NULL COMMENT '本趟唯一车辆资源记录ID',
|
trip_id BIGINT NOT NULL COMMENT '本趟唯一车辆资源记录ID',
|
||||||
overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '真实起运海外仓ID',
|
overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '真实起运海外仓ID',
|
||||||
overseas_warehouse_code VARCHAR(128) NULL COMMENT '起运海外仓编码快照',
|
overseas_warehouse_code VARCHAR(128) NULL COMMENT '起运海外仓编码快照',
|
||||||
|
|||||||
@@ -4080,10 +4080,6 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
x-java-record: DispatchCommands.Update
|
x-java-record: DispatchCommands.Update
|
||||||
properties:
|
properties:
|
||||||
publishLooseboardDemand:
|
|
||||||
type: boolean
|
|
||||||
description: 是否向内部散板需求池发布本车余位;新建默认勾选。
|
|
||||||
|
|
||||||
recordVersion:
|
recordVersion:
|
||||||
oneOf:
|
oneOf:
|
||||||
- type: string
|
- type: string
|
||||||
@@ -4118,7 +4114,6 @@ components:
|
|||||||
nullable: true
|
nullable: true
|
||||||
description: 本趟可空补充资料,八字段均可空;空白字符串规范为null,不替代正式FBA预约或实际发车时间。
|
description: 本趟可空补充资料,八字段均可空;空白字符串规范为null,不替代正式FBA预约或实际发车时间。
|
||||||
required:
|
required:
|
||||||
- publishLooseboardDemand
|
|
||||||
- recordVersion
|
- recordVersion
|
||||||
- idempotencyKey
|
- idempotencyKey
|
||||||
DeliveryStatusChange:
|
DeliveryStatusChange:
|
||||||
@@ -5984,8 +5979,8 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
publishLooseboardDemand:
|
publishLooseboardDemand:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 是否向内部散板需求池发布本车余位;新建默认勾选。
|
description: 是否向内部散板需求池发布本车余位;仅智能排车建单时选择,默认不发布。
|
||||||
default: true
|
default: false
|
||||||
|
|
||||||
idempotencyKey:
|
idempotencyKey:
|
||||||
type: string
|
type: string
|
||||||
@@ -6701,7 +6696,7 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
publishLooseboardDemand:
|
publishLooseboardDemand:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 是否向内部散板需求池发布本车余位;新建默认勾选。
|
description: 建单时确定的散板需求发布选择,维护车辆不可修改。
|
||||||
|
|
||||||
dispatchNo:
|
dispatchNo:
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
+1
-1
@@ -123,7 +123,7 @@ class PlatformDispatchOutboundContractTest {
|
|||||||
assertTrue(Arrays.stream(DispatchController.class.getDeclaredMethods())
|
assertTrue(Arrays.stream(DispatchController.class.getDeclaredMethods())
|
||||||
.noneMatch(method -> Set.of("depart", "cancel").contains(method.getName())));
|
.noneMatch(method -> Set.of("depart", "cancel").contains(method.getName())));
|
||||||
assertEquals(Set.of("recordVersion", "idempotencyKey", "vehicle", "driver",
|
assertEquals(Set.of("recordVersion", "idempotencyKey", "vehicle", "driver",
|
||||||
"plannedDepartureAt", "estimatedArrivalAt", "remark", "publishLooseboardDemand", "maintenance"),
|
"plannedDepartureAt", "estimatedArrivalAt", "remark", "maintenance"),
|
||||||
Arrays.stream(DispatchCommands.Update.class.getRecordComponents())
|
Arrays.stream(DispatchCommands.Update.class.getRecordComponents())
|
||||||
.map(component -> component.getName()).collect(java.util.stream.Collectors.toSet()));
|
.map(component -> component.getName()).collect(java.util.stream.Collectors.toSet()));
|
||||||
assertEquals(Set.of("recordVersion", "idempotencyKey", "reason"),
|
assertEquals(Set.of("recordVersion", "idempotencyKey", "reason"),
|
||||||
|
|||||||
+1
@@ -36,6 +36,7 @@ public final class DispatchPortCompileContract {
|
|||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
require(expected.getMessage().contains("recordVersion"), "明确说明缺失版本");
|
require(expected.getMessage().contains("recordVersion"), "明确说明缺失版本");
|
||||||
}
|
}
|
||||||
|
require(!fields(DispatchCommands.Update.class).contains("publishLooseboardDemand"), "发布选择仅在创建时确定,维护车辆不得修改");
|
||||||
require(!fields(DispatchCommands.Update.class).contains("vehicleAppointment"), "更新不保留车辆预约字段或空占位");
|
require(!fields(DispatchCommands.Update.class).contains("vehicleAppointment"), "更新不保留车辆预约字段或空占位");
|
||||||
require(!fields(DispatchViews.DispatchInfo.class).contains("vehicleAppointment"), "排车信息不保留车辆预约字段或空占位");
|
require(!fields(DispatchViews.DispatchInfo.class).contains("vehicleAppointment"), "排车信息不保留车辆预约字段或空占位");
|
||||||
require(fields(DispatchViews.DispatchInfo.class).containsAll(Set.of("fbaAppointments","appointmentBatches")), "原FBA预约及批次仍保留");
|
require(fields(DispatchViews.DispatchInfo.class).containsAll(Set.of("fbaAppointments","appointmentBatches")), "原FBA预约及批次仍保留");
|
||||||
|
|||||||
+3
-1
@@ -39,12 +39,14 @@ public final class DispatchPublicJsonContract {
|
|||||||
rejectsMissing(requests, request, "{}");
|
rejectsMissing(requests, request, "{}");
|
||||||
rejectsMissing(requests, request, "{\"recordVersion\":null}");
|
rejectsMissing(requests, request, "{\"recordVersion\":null}");
|
||||||
rejectsMissing(requests, request, "{\"recordVersion\":-1}");
|
rejectsMissing(requests, request, "{\"recordVersion\":-1}");
|
||||||
Object zero = requests.readValue(request == DispatchCommands.Update.class ? "{\"recordVersion\":0,\"publishLooseboardDemand\":true}" : "{\"recordVersion\":0}", request);
|
Object zero = requests.readValue("{\"recordVersion\":0}", request);
|
||||||
require(Long.valueOf(0).equals(request.getMethod("recordVersion").invoke(zero)), "显式零版本必须合法");
|
require(Long.valueOf(0).equals(request.getMethod("recordVersion").invoke(zero)), "显式零版本必须合法");
|
||||||
}
|
}
|
||||||
rejectsMissing(requests, DispatchCommands.Update.class,
|
rejectsMissing(requests, DispatchCommands.Update.class,
|
||||||
"{\"recordVersion\":0,\"vehicleAppointment\":{\"referenceNo\":\"REMOVED\",\"recordVersion\":0}}");
|
"{\"recordVersion\":0,\"vehicleAppointment\":{\"referenceNo\":\"REMOVED\",\"recordVersion\":0}}");
|
||||||
|
|
||||||
|
rejectsMissing(requests, DispatchCommands.Update.class, "{\"recordVersion\":0,\"publishLooseboardDemand\":true}");
|
||||||
|
|
||||||
Instant now = Instant.parse("2026-10-19T00:00:00Z");
|
Instant now = Instant.parse("2026-10-19T00:00:00Z");
|
||||||
var outbound = new OutboundReference("101", "客户甲", "OB-CONTRACT", "OUTBOUNDED", 10, 1);
|
var outbound = new OutboundReference("101", "客户甲", "OB-CONTRACT", "OUTBOUNDED", 10, 1);
|
||||||
var destination = new Destination("FBA:ONT8", "FBA", "1", "ONT8", "目的仓", null, null, null, null);
|
var destination = new Destination("FBA:ONT8", "FBA", "1", "ONT8", "目的仓", null, null, null, null);
|
||||||
|
|||||||
+2
-2
@@ -249,7 +249,7 @@ public class DispatchAggregatePersistenceTest {
|
|||||||
/** 空单编辑保留零货量与版本命令,不借编辑隐式制造可装车来源。 */
|
/** 空单编辑保留零货量与版本命令,不借编辑隐式制造可装车来源。 */
|
||||||
@Test void emptyShellEditPreservesZeroCargoAndVersionedReplay() throws Exception {
|
@Test void emptyShellEditPreservesZeroCargoAndVersionedReplay() throws Exception {
|
||||||
var f=new Fixture();var created=f.tx.execute(t->f.service.create(f.actor,f.create("empty-edit",List.of())));
|
var f=new Fixture();var created=f.tx.execute(t->f.service.create(f.actor,f.create("empty-edit",List.of())));
|
||||||
var request=new DispatchCommands.Update(0L,"empty-edit-original",null,null,null,null,"空单补充备注", true, null);
|
var request=new DispatchCommands.Update(0L,"empty-edit-original",null,null,null,null,"空单补充备注", null);
|
||||||
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),request));
|
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),request));
|
||||||
assertThat(saved.stage()).isEqualTo(Stage.BEFORE_LOADING);assertThat(saved.counts().packageCount()).isZero();
|
assertThat(saved.stage()).isEqualTo(Stage.BEFORE_LOADING);assertThat(saved.counts().packageCount()).isZero();
|
||||||
assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
||||||
@@ -289,7 +289,7 @@ public class DispatchAggregatePersistenceTest {
|
|||||||
}
|
}
|
||||||
@Test void updateSavesCurrentVehiclePlanAndReplayWithoutAnyVehicleAppointmentField() throws Exception {
|
@Test void updateSavesCurrentVehiclePlanAndReplayWithoutAnyVehicleAppointmentField() throws Exception {
|
||||||
var f=new Fixture();var input=f.configured("update-without-vehicle-appointment",List.of(f.source(1,"101",501,"ONT8",3)));var created=f.tx.execute(t->f.service.create(f.actor,input));
|
var f=new Fixture();var input=f.configured("update-without-vehicle-appointment",List.of(f.source(1,"101",501,"ONT8",3)));var created=f.tx.execute(t->f.service.create(f.actor,input));
|
||||||
var arrival=input.plannedDepartureAt().plusSeconds(3600);var request=new DispatchCommands.Update(created.recordVersion(),"update-current-plan",input.vehicle(),input.driver(),input.plannedDepartureAt(),arrival,"调整本趟计划", true, null);
|
var arrival=input.plannedDepartureAt().plusSeconds(3600);var request=new DispatchCommands.Update(created.recordVersion(),"update-current-plan",input.vehicle(),input.driver(),input.plannedDepartureAt(),arrival,"调整本趟计划", null);
|
||||||
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),request));assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),request));assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
||||||
var detail=f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo()));assertThat(detail.dispatchInfo().estimatedArrivalAt()).isEqualTo(arrival);assertThat(detail.documents().remark()).isEqualTo("调整本趟计划");
|
var detail=f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo()));assertThat(detail.dispatchInfo().estimatedArrivalAt()).isEqualTo(arrival);assertThat(detail.documents().remark()).isEqualTo("调整本趟计划");
|
||||||
var publicJson=f.json.valueToTree(detail);assertThat(publicJson.path("dispatchInfo").has("vehicleAppointment")).isFalse();assertThat(publicJson.path("dispatchInfo").has("fbaAppointments")).isTrue();
|
var publicJson=f.json.valueToTree(detail);assertThat(publicJson.path("dispatchInfo").has("vehicleAppointment")).isFalse();assertThat(publicJson.path("dispatchInfo").has("fbaAppointments")).isTrue();
|
||||||
|
|||||||
+23
-8
@@ -9,22 +9,37 @@ import java.util.List;
|
|||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
class DispatchCapacityDemandPersistenceTest {
|
class DispatchCapacityDemandPersistenceTest {
|
||||||
/** 默认发布和取消发布随原排车命令同事务保存,原请求重放不重复修改。 */
|
/** 未选择发布的新排车保持私有,维护与重放均不能开启发布。 */
|
||||||
@Test void defaultsAndUnpublishesThroughTheExistingVersionedCommand() throws Exception {
|
@Test void defaultsToUnpublishedAndMaintenanceCannotEnablePublication() throws Exception {
|
||||||
var f=new DispatchAggregatePersistenceTest.Fixture();
|
var f=new DispatchAggregatePersistenceTest.Fixture();
|
||||||
var request=f.json.readValue("{\"idempotencyKey\":\"pool-default\",\"warehouseId\":\"9\",\"cargo\":[],\"batchAllocations\":[]}",DispatchCommands.Create.class);
|
var request=f.json.readValue("{\"idempotencyKey\":\"pool-default\",\"warehouseId\":\"9\",\"cargo\":[],\"batchAllocations\":[]}",DispatchCommands.Create.class);
|
||||||
var created=f.tx.execute(s->f.service.create(f.actor,request));
|
var created=f.tx.execute(s->f.service.create(f.actor,request));
|
||||||
var service=new DispatchCapacityDemandService(f.repository,f.json);
|
var service=new DispatchCapacityDemandService(f.repository,f.json);
|
||||||
assertThat(service.page(query()).records()).extracting(DispatchCapacityDemands.Demand::dispatchNo).containsExactly(created.dispatchNo());
|
|
||||||
assertThat(f.service.detail(f.actor,created.dispatchNo()).publishLooseboardDemand()).isTrue();
|
|
||||||
var update=new DispatchCommands.Update(created.recordVersion(),"pool-disable",null,null,null,null,null,false, null);
|
|
||||||
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
|
||||||
assertThat(f.service.detail(f.actor,created.dispatchNo()).publishLooseboardDemand()).isFalse();
|
assertThat(f.service.detail(f.actor,created.dispatchNo()).publishLooseboardDemand()).isFalse();
|
||||||
assertThat(service.page(query()).total()).isZero();
|
var update=new DispatchCommands.Update(created.recordVersion(),"pool-maintain-private",null,null,null,null,"补充资料",null);
|
||||||
|
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
var replay=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
var replay=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
assertThat(replay.recordVersion()).isEqualTo(saved.recordVersion());
|
assertThat(replay.recordVersion()).isEqualTo(saved.recordVersion());
|
||||||
|
assertThat(f.service.detail(f.actor,created.dispatchNo()).publishLooseboardDemand()).isFalse();
|
||||||
assertThat(service.page(query()).total()).isZero();
|
assertThat(service.page(query()).total()).isZero();
|
||||||
}
|
}
|
||||||
|
/** 建单发布后维护不改变发布;重发须取消原排车,再建新单。 */
|
||||||
|
@Test void preservesPublicationUntilCancellationAndRepublishesOnlyOnNewDispatch() throws Exception {
|
||||||
|
var f=new DispatchAggregatePersistenceTest.Fixture();
|
||||||
|
var created=f.tx.execute(s->f.service.create(f.actor,f.create("pool-publish",List.of())));
|
||||||
|
var service=new DispatchCapacityDemandService(f.repository,f.json);
|
||||||
|
var update=new DispatchCommands.Update(created.recordVersion(),"pool-maintain-public",null,null,null,null,"补充资料",null);
|
||||||
|
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
|
var replay=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
|
assertThat(replay.recordVersion()).isEqualTo(saved.recordVersion());
|
||||||
|
assertThat(f.service.detail(f.actor,created.dispatchNo()).publishLooseboardDemand()).isTrue();
|
||||||
|
assertThat(service.page(query()).records()).extracting(DispatchCapacityDemands.Demand::dispatchNo).containsExactly(created.dispatchNo());
|
||||||
|
f.tx.execute(s->f.service.cancel(f.actor,created.dispatchNo(),new DispatchCommands.ReasonCommand(saved.recordVersion(),"pool-cancel","取消后重新创建")));
|
||||||
|
assertThat(service.page(query()).total()).isZero();
|
||||||
|
var recreated=f.tx.execute(s->f.service.create(f.actor,f.create("pool-republish",List.of())));
|
||||||
|
assertThat(recreated.dispatchNo()).isNotEqualTo(created.dispatchNo());
|
||||||
|
assertThat(service.page(query()).records()).extracting(DispatchCapacityDemands.Demand::dispatchNo).containsExactly(recreated.dispatchNo());
|
||||||
|
}
|
||||||
/** 余位缺测时保持未知;不能将实托数量当标准托位或使用默认三十托。 */
|
/** 余位缺测时保持未知;不能将实托数量当标准托位或使用默认三十托。 */
|
||||||
@Test void unknownCapacityIsVisibleAndNonDeliveryOrDepartedRowsAreExcluded() throws Exception {
|
@Test void unknownCapacityIsVisibleAndNonDeliveryOrDepartedRowsAreExcluded() throws Exception {
|
||||||
var f=new DispatchAggregatePersistenceTest.Fixture();
|
var f=new DispatchAggregatePersistenceTest.Fixture();
|
||||||
@@ -63,7 +78,7 @@ class DispatchCapacityDemandPersistenceTest {
|
|||||||
f.db.update("UPDATE ll_dispatch_order SET loading_confirmation_json=?",f.json.writeValueAsString(load));
|
f.db.update("UPDATE ll_dispatch_order SET loading_confirmation_json=?",f.json.writeValueAsString(load));
|
||||||
assertThat(new DispatchCapacityDemandService(f.repository,f.json).page(query()).total()).isZero();
|
assertThat(new DispatchCapacityDemandService(f.repository,f.json).page(query()).total()).isZero();
|
||||||
}
|
}
|
||||||
/** 新建时可以显式取消默认发布,后续不产生额外发布记录。 */
|
/** 新建时显式不发布,查询只按真实仓库与路线筛选。 */
|
||||||
@Test void createCanOptOutAndFiltersUseTheActualWarehouseAndRoute() throws Exception {
|
@Test void createCanOptOutAndFiltersUseTheActualWarehouseAndRoute() throws Exception {
|
||||||
var f=new DispatchAggregatePersistenceTest.Fixture();var initial=f.create("pool-opt-out",List.of());
|
var f=new DispatchAggregatePersistenceTest.Fixture();var initial=f.create("pool-opt-out",List.of());
|
||||||
var request=new DispatchCommands.Create(initial.idempotencyKey(),initial.warehouseId(),null,null,null,null,null,List.of(),List.of(),false);
|
var request=new DispatchCommands.Create(initial.idempotencyKey(),initial.warehouseId(),null,null,null,null,null,List.of(),List.of(),false);
|
||||||
|
|||||||
+3
-3
@@ -14,7 +14,7 @@ class DispatchDestinationAppointmentPersistenceTest {
|
|||||||
var created=f.tx.execute(t->f.service.create(f.actor,input));
|
var created=f.tx.execute(t->f.service.create(f.actor,input));
|
||||||
assertThat(f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo())).dispatchInfo().fbaAppointments()).hasSize(1);
|
assertThat(f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo())).dispatchInfo().fbaAppointments()).hasSize(1);
|
||||||
assertThatThrownBy(()->f.tx.execute(t->f.service.submitReview(f.actor,created.dispatchNo(),new DispatchCommands.SubmitReview(created.recordVersion(),"missing",null)))).hasMessageContaining("ONT8");
|
assertThatThrownBy(()->f.tx.execute(t->f.service.submitReview(f.actor,created.dispatchNo(),new DispatchCommands.SubmitReview(created.recordVersion(),"missing",null)))).hasMessageContaining("ONT8");
|
||||||
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"dest-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,true,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"))))));
|
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"dest-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"))))));
|
||||||
var loaded=f.approve(saved);assertThat(loaded.stage()).isEqualTo(Stage.LOADING);
|
var loaded=f.approve(saved);assertThat(loaded.stage()).isEqualTo(Stage.LOADING);
|
||||||
var detail=f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo()));
|
var detail=f.tx.execute(t->f.service.detail(f.actor,created.dispatchNo()));
|
||||||
assertThat(detail.dispatchInfo().fbaAppointments()).singleElement().satisfies(a->assertThat(a.appointmentNo()).isEqualTo("ISA-ONE"));
|
assertThat(detail.dispatchInfo().fbaAppointments()).singleElement().satisfies(a->assertThat(a.appointmentNo()).isEqualTo("ISA-ONE"));
|
||||||
@@ -23,9 +23,9 @@ class DispatchDestinationAppointmentPersistenceTest {
|
|||||||
@Test void savingOneOfTwoNumbersIsAllowedButReviewNeedsBoth() throws Exception {
|
@Test void savingOneOfTwoNumbersIsAllowedButReviewNeedsBoth() throws Exception {
|
||||||
var f=new DispatchAggregatePersistenceTest.Fixture();var input=f.configured("two-dest-create",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"202",502,"LGB8",5)));
|
var f=new DispatchAggregatePersistenceTest.Fixture();var input=f.configured("two-dest-create",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"202",502,"LGB8",5)));
|
||||||
var created=f.tx.execute(t->f.service.create(f.actor,input));
|
var created=f.tx.execute(t->f.service.create(f.actor,input));
|
||||||
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"partial-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,true,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"))))));
|
var saved=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"partial-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"))))));
|
||||||
assertThatThrownBy(()->f.tx.execute(t->f.service.submitReview(f.actor,created.dispatchNo(),new DispatchCommands.SubmitReview(saved.recordVersion(),"second-missing",null)))).hasMessageContaining("LGB8");
|
assertThatThrownBy(()->f.tx.execute(t->f.service.submitReview(f.actor,created.dispatchNo(),new DispatchCommands.SubmitReview(saved.recordVersion(),"second-missing",null)))).hasMessageContaining("LGB8");
|
||||||
var complete=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(saved.recordVersion(),"complete-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,true,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"),new DestinationAppointment("FBA:LGB8","ISA-TWO"))))));
|
var complete=f.tx.execute(t->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(saved.recordVersion(),"complete-save",input.vehicle(),input.driver(),input.plannedDepartureAt(),null,null,new Maintenance(null,null,null,null,null,null,null,List.of(new DestinationAppointment("FBA:ONT8","ISA-ONE"),new DestinationAppointment("FBA:LGB8","ISA-TWO"))))));
|
||||||
assertThat(f.approve(complete).stage()).isEqualTo(Stage.LOADING);
|
assertThat(f.approve(complete).stage()).isEqualTo(Stage.LOADING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -33,7 +33,7 @@ class DispatchFeedbackRegressionTest {
|
|||||||
var input=f.configured("update",List.of());
|
var input=f.configured("update",List.of());
|
||||||
var update=new DispatchCommands.Update(created.recordVersion(),"save-master",input.vehicle(),
|
var update=new DispatchCommands.Update(created.recordVersion(),"save-master",input.vehicle(),
|
||||||
new DispatchCommands.Driver(ResourceMode.MASTER,71L,null,null,new DispatchContracts.DriverEvidence(null,null,List.of(91L),input.driver().evidence().expectedTaskEndAt(),List.of(303L))),
|
new DispatchCommands.Driver(ResourceMode.MASTER,71L,null,null,new DispatchContracts.DriverEvidence(null,null,List.of(91L),input.driver().evidence().expectedTaskEndAt(),List.of(303L))),
|
||||||
input.plannedDepartureAt(),null,null,true,null);
|
input.plannedDepartureAt(),null,null,null);
|
||||||
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
f.prepareAppointmentNumbers(created.dispatchNo());
|
f.prepareAppointmentNumbers(created.dispatchNo());
|
||||||
var proof=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver().evidence();
|
var proof=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver().evidence();
|
||||||
@@ -78,7 +78,7 @@ class DispatchFeedbackRegressionTest {
|
|||||||
var update=new DispatchCommands.Update(created.recordVersion(),"save-draft",
|
var update=new DispatchCommands.Update(created.recordVersion(),"save-draft",
|
||||||
new DispatchCommands.Vehicle(v.mode(),null,v.plate(),v.carrierName(),v.capacity(),List.of()),
|
new DispatchCommands.Vehicle(v.mode(),null,v.plate(),v.carrierName(),v.capacity(),List.of()),
|
||||||
new DispatchCommands.Driver(d.mode(),null,d.name(),d.phone(),new DispatchContracts.DriverEvidence(e.licenseNo(),e.licenseExpiresOn(),List.of(),e.expectedTaskEndAt(),List.of())),
|
new DispatchCommands.Driver(d.mode(),null,d.name(),d.phone(),new DispatchContracts.DriverEvidence(e.licenseNo(),e.licenseExpiresOn(),List.of(),e.expectedTaskEndAt(),List.of())),
|
||||||
input.plannedDepartureAt(),null,null,true,null);
|
input.plannedDepartureAt(),null,null,null);
|
||||||
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
var saved=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),update));
|
||||||
assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
assertThat(saved.recordVersion()).isGreaterThan(created.recordVersion());
|
||||||
f.prepareAppointmentNumbers(created.dispatchNo());
|
f.prepareAppointmentNumbers(created.dispatchNo());
|
||||||
|
|||||||
+2
-2
@@ -66,13 +66,13 @@ class DispatchListAndResourceRegressionTest {
|
|||||||
when(drivers.requireDispatchable(eq(71L),any())).thenReturn(driver);
|
when(drivers.requireDispatchable(eq(71L),any())).thenReturn(driver);
|
||||||
var input=f.configured("input",List.of());
|
var input=f.configured("input",List.of());
|
||||||
var requestDriver=new DispatchCommands.Driver(ResourceMode.MASTER,71L,null,null,new DispatchContracts.DriverEvidence(null,null,List.of(91L),input.driver().evidence().expectedTaskEndAt(),List.of(303L)));
|
var requestDriver=new DispatchCommands.Driver(ResourceMode.MASTER,71L,null,null,new DispatchContracts.DriverEvidence(null,null,List.of(91L),input.driver().evidence().expectedTaskEndAt(),List.of(303L)));
|
||||||
var first=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"first",input.vehicle(),requestDriver,input.plannedDepartureAt(),null,null,true,null)));
|
var first=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(created.recordVersion(),"first",input.vehicle(),requestDriver,input.plannedDepartureAt(),null,null,null)));
|
||||||
var snapshot=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver();var proof=snapshot.evidence();
|
var snapshot=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver();var proof=snapshot.evidence();
|
||||||
when(driver.licenseFileId()).thenReturn("82");
|
when(driver.licenseFileId()).thenReturn("82");
|
||||||
var evidence=(DispatchEvidenceGateway)ReflectionTestUtils.getField(f.service,"evidence");clearInvocations(evidence);
|
var evidence=(DispatchEvidenceGateway)ReflectionTestUtils.getField(f.service,"evidence");clearInvocations(evidence);
|
||||||
doThrow(new IllegalArgumentException("旧主档证照不能按本车附件读取")).when(evidence).metadata(anyLong(),eq(DispatchEvidenceGateway.Kind.DRIVER),eq(81L));
|
doThrow(new IllegalArgumentException("旧主档证照不能按本车附件读取")).when(evidence).metadata(anyLong(),eq(DispatchEvidenceGateway.Kind.DRIVER),eq(81L));
|
||||||
var changedDriver=new DispatchCommands.Driver(ResourceMode.MASTER,71L,snapshot.name(),snapshot.phone(),new DispatchContracts.DriverEvidence(proof.licenseNo(),proof.licenseExpiresOn(),proof.fileIds(),proof.expectedTaskEndAt(),proof.photoFileIds(),91L));
|
var changedDriver=new DispatchCommands.Driver(ResourceMode.MASTER,71L,snapshot.name(),snapshot.phone(),new DispatchContracts.DriverEvidence(proof.licenseNo(),proof.licenseExpiresOn(),proof.fileIds(),proof.expectedTaskEndAt(),proof.photoFileIds(),91L));
|
||||||
f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(first.recordVersion(),"second",input.vehicle(),changedDriver,input.plannedDepartureAt(),null,null,true,null)));
|
f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(first.recordVersion(),"second",input.vehicle(),changedDriver,input.plannedDepartureAt(),null,null,null)));
|
||||||
var saved=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver().evidence();
|
var saved=f.service.detail(f.actor,created.dispatchNo()).vehicleInfo().driver().evidence();
|
||||||
assertThat(saved.fileIds()).containsExactly(82L,91L);assertThat(saved.masterLicenseFileId()).isEqualTo(82L);
|
assertThat(saved.fileIds()).containsExactly(82L,91L);assertThat(saved.masterLicenseFileId()).isEqualTo(82L);
|
||||||
verify(evidence,never()).metadata(anyLong(),eq(DispatchEvidenceGateway.Kind.DRIVER),eq(81L));
|
verify(evidence,never()).metadata(anyLong(),eq(DispatchEvidenceGateway.Kind.DRIVER),eq(81L));
|
||||||
|
|||||||
+5
-5
@@ -31,7 +31,7 @@ class DispatchMaintenancePersistenceTest {
|
|||||||
var maintenance = new Maintenance(LocalDate.of(2026, 11, 1), " FBA自提 ", "一提多卸", "混装",
|
var maintenance = new Maintenance(LocalDate.of(2026, 11, 1), " FBA自提 ", "一提多卸", "混装",
|
||||||
" fleet@example.test ", "厢式货车", " account ", List.of(new com.baiyue.lastleg.dispatch.port.DispatchContracts.DestinationAppointment("FBA:ONT8", " appointment ")));
|
" fleet@example.test ", "厢式货车", " account ", List.of(new com.baiyue.lastleg.dispatch.port.DispatchContracts.DestinationAppointment("FBA:ONT8", " appointment ")));
|
||||||
var request = new DispatchCommands.Update(created.recordVersion(), "maintenance-save", vehicle, input.driver(),
|
var request = new DispatchCommands.Update(created.recordVersion(), "maintenance-save", vehicle, input.driver(),
|
||||||
input.plannedDepartureAt(), input.estimatedArrivalAt(), "保留原备注", true, maintenance);
|
input.plannedDepartureAt(), input.estimatedArrivalAt(), "保留原备注", maintenance);
|
||||||
var saved = f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request));
|
var saved = f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request));
|
||||||
assertThat(f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request)).replayed()).isTrue();
|
assertThat(f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request)).replayed()).isTrue();
|
||||||
f.approve(saved);
|
f.approve(saved);
|
||||||
@@ -63,7 +63,7 @@ class DispatchMaintenancePersistenceTest {
|
|||||||
var dispatchId = f.repository.find(created.dispatchNo()).getId();
|
var dispatchId = f.repository.find(created.dispatchNo()).getId();
|
||||||
doThrow(new IllegalArgumentException(reason)).when(files(f)).metadata(dispatchId, DispatchEvidenceGateway.Kind.VEHICLE, 301L);
|
doThrow(new IllegalArgumentException(reason)).when(files(f)).metadata(dispatchId, DispatchEvidenceGateway.Kind.VEHICLE, 301L);
|
||||||
var request = new DispatchCommands.Update(created.recordVersion(), "invalid-vehicle-save", vehicle(f, "MASTER", List.of(301L)),
|
var request = new DispatchCommands.Update(created.recordVersion(), "invalid-vehicle-save", vehicle(f, "MASTER", List.of(301L)),
|
||||||
input.driver(), input.plannedDepartureAt(), null, null, true, null);
|
input.driver(), input.plannedDepartureAt(), null, null, null);
|
||||||
assertThatThrownBy(() -> f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request))).hasMessageContaining(reason);
|
assertThatThrownBy(() -> f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), request))).hasMessageContaining(reason);
|
||||||
assertThat(f.db.queryForMap("SELECT resources_json,record_version FROM ll_dispatch_order WHERE dispatch_no=?", created.dispatchNo())).isEqualTo(before);
|
assertThat(f.db.queryForMap("SELECT resources_json,record_version FROM ll_dispatch_order WHERE dispatch_no=?", created.dispatchNo())).isEqualTo(before);
|
||||||
assertThat(f.db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_command WHERE idempotency_key='invalid-vehicle-save'", Long.class)).isZero();
|
assertThat(f.db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_command WHERE idempotency_key='invalid-vehicle-save'", Long.class)).isZero();
|
||||||
@@ -76,9 +76,9 @@ class DispatchMaintenancePersistenceTest {
|
|||||||
var empty = f.json.readValue("{\"plannedDeliveryDate\":\" \",\"deliveryMethod\":\" \",\"pickupMethod\":\"\",\"loadType\":\" \",\"fleetEmail\":\" \",\"vehicleType\":\"\",\"appointmentAccount\":\" \",\"destinationAppointments\":[]}", Maintenance.class);
|
var empty = f.json.readValue("{\"plannedDeliveryDate\":\" \",\"deliveryMethod\":\" \",\"pickupMethod\":\"\",\"loadType\":\" \",\"fleetEmail\":\" \",\"vehicleType\":\"\",\"appointmentAccount\":\" \",\"destinationAppointments\":[]}", Maintenance.class);
|
||||||
assertThat(empty).isEqualTo(new Maintenance(null, null, null, null, null, null, null, null));
|
assertThat(empty).isEqualTo(new Maintenance(null, null, null, null, null, null, null, null));
|
||||||
var saved = f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(
|
var saved = f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(
|
||||||
created.recordVersion(), "empty-maintenance-save", null, null, null, null, null, true, empty)));
|
created.recordVersion(), "empty-maintenance-save", null, null, null, null, null, empty)));
|
||||||
f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(
|
f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(
|
||||||
saved.recordVersion(), "empty-maintenance-clear", null, null, null, null, null, true, null)));
|
saved.recordVersion(), "empty-maintenance-clear", null, null, null, null, null, null)));
|
||||||
var detail = f.tx.execute(t -> f.service.detail(f.actor, created.dispatchNo()));
|
var detail = f.tx.execute(t -> f.service.detail(f.actor, created.dispatchNo()));
|
||||||
assertThat(detail.vehicleInfo().maintenance()).isNull();
|
assertThat(detail.vehicleInfo().maintenance()).isNull();
|
||||||
assertThat(detail.dispatchInfo().departedAt()).isNull();
|
assertThat(detail.dispatchInfo().departedAt()).isNull();
|
||||||
@@ -112,7 +112,7 @@ class DispatchMaintenancePersistenceTest {
|
|||||||
var created = f.tx.execute(t -> f.service.create(f.actor, input));
|
var created = f.tx.execute(t -> f.service.create(f.actor, input));
|
||||||
var maintenance = new Maintenance(LocalDate.of(2026, 11, 1), "外配", "一提一卸", "地板", null, "平板车", null, List.of());
|
var maintenance = new Maintenance(LocalDate.of(2026, 11, 1), "外配", "一提一卸", "地板", null, "平板车", null, List.of());
|
||||||
f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(created.recordVersion(),
|
f.tx.execute(t -> f.service.update(f.actor, created.dispatchNo(), new DispatchCommands.Update(created.recordVersion(),
|
||||||
"projection-maintenance-save", vehicle(f, "MASTER", List.of(301L)), input.driver(), input.plannedDepartureAt(), null, null, true, maintenance)));
|
"projection-maintenance-save", vehicle(f, "MASTER", List.of(301L)), input.driver(), input.plannedDepartureAt(), null, null, maintenance)));
|
||||||
var projection = new com.baiyue.lastleg.dispatch.transport.TransportTaskProjection(f.repository,
|
var projection = new com.baiyue.lastleg.dispatch.transport.TransportTaskProjection(f.repository,
|
||||||
mock(com.baiyue.lastleg.dispatch.transport.TransportTaskRepository.class),
|
mock(com.baiyue.lastleg.dispatch.transport.TransportTaskRepository.class),
|
||||||
mock(com.baiyue.lastleg.trucking.repository.UsTruckOrderRepository.class), mock(DispatchNumberGenerator.class), f.json);
|
mock(com.baiyue.lastleg.trucking.repository.UsTruckOrderRepository.class), mock(DispatchNumberGenerator.class), f.json);
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ class DispatchResourcePhotosPersistenceTest {
|
|||||||
var e = d.evidence();
|
var e = d.evidence();
|
||||||
var driver = new DispatchCommands.Driver(d.mode(), d.driverId(), d.name(), d.phone(),
|
var driver = new DispatchCommands.Driver(d.mode(), d.driverId(), d.name(), d.phone(),
|
||||||
new DispatchContracts.DriverEvidence(e.licenseNo(), e.licenseExpiresOn(), e.fileIds(), e.expectedTaskEndAt(), photos));
|
new DispatchContracts.DriverEvidence(e.licenseNo(), e.licenseExpiresOn(), e.fileIds(), e.expectedTaskEndAt(), photos));
|
||||||
return new DispatchCommands.Update(version, key, input.vehicle(), driver, input.plannedDepartureAt(), input.estimatedArrivalAt(), "维护照片", true, null);
|
return new DispatchCommands.Update(version, key, input.vehicle(), driver, input.plannedDepartureAt(), input.estimatedArrivalAt(), "维护照片", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 只替代外部附件提供者,资源、命令和事务仍走真实服务。 */
|
/** 只替代外部附件提供者,资源、命令和事务仍走真实服务。 */
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ class DispatchReviewWorkflowPersistenceTest {
|
|||||||
long item=f.db.queryForObject("SELECT id FROM ll_dispatch_item WHERE plan_pallet_id=3",Long.class);
|
long item=f.db.queryForObject("SELECT id FROM ll_dispatch_item WHERE plan_pallet_id=3",Long.class);
|
||||||
var removed=f.tx.execute(s->f.service.removeCargo(f.actor,created.dispatchNo(),item,new DispatchCommands.ReasonCommand(addedAgain.recordVersion(),"remove","调整目的仓")));
|
var removed=f.tx.execute(s->f.service.removeCargo(f.actor,created.dispatchNo(),item,new DispatchCommands.ReasonCommand(addedAgain.recordVersion(),"remove","调整目的仓")));
|
||||||
var resources=f.configured("updated-resources",List.of());
|
var resources=f.configured("updated-resources",List.of());
|
||||||
var updated=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(removed.recordVersion(),"update-vehicle",resources.vehicle(),resources.driver(),resources.plannedDepartureAt().plusSeconds(60),null,"客服更正", true, null)));
|
var updated=f.tx.execute(s->f.service.update(f.actor,created.dispatchNo(),new DispatchCommands.Update(removed.recordVersion(),"update-vehicle",resources.vehicle(),resources.driver(),resources.plannedDepartureAt().plusSeconds(60),null,"客服更正", null)));
|
||||||
var detail=f.service.detail(f.actor,created.dispatchNo());
|
var detail=f.service.detail(f.actor,created.dispatchNo());
|
||||||
var retained=loading?detail.reviews().loadingReview():detail.reviews().dispatchReview();
|
var retained=loading?detail.reviews().loadingReview():detail.reviews().dispatchReview();
|
||||||
assertThat(detail.reviewState()).isEqualTo(ReviewState.DRAFT);
|
assertThat(detail.reviewState()).isEqualTo(ReviewState.DRAFT);
|
||||||
|
|||||||
+2
@@ -35,6 +35,8 @@ class AppointmentWorkPreArrangementPersistenceTest {
|
|||||||
assertEquals("FAILED",batch.appointmentStatus());var originalB=store.plan(batch.batchKey(),"B");
|
assertEquals("FAILED",batch.appointmentStatus());var originalB=store.plan(batch.batchKey(),"B");
|
||||||
var session=context.getBean(SqlSessionTemplate.class);session.getConfiguration().addMapper(AppointmentWorkDispatchMapper.class);
|
var session=context.getBean(SqlSessionTemplate.class);session.getConfiguration().addMapper(AppointmentWorkDispatchMapper.class);
|
||||||
var mapper=spy(session.getMapper(AppointmentWorkDispatchMapper.class));
|
var mapper=spy(session.getMapper(AppointmentWorkDispatchMapper.class));
|
||||||
|
// 本场景尚未扫码;保持真实已拣为空,不能借预排数量伪造出库事实。
|
||||||
|
doReturn(List.of()).when(mapper).pickedPhysical(anyLong(),anyString());
|
||||||
doReturn(List.of(new AppointmentWorkDispatchMapper.Candidate(101L,"P-B",11L,"IP-B","B","RECEIVED",false),new AppointmentWorkDispatchMapper.Candidate(102L,"P-D",12L,"IP-D","D","RECEIVED",false))).when(mapper).candidates(batch.id());
|
doReturn(List.of(new AppointmentWorkDispatchMapper.Candidate(101L,"P-B",11L,"IP-B","B","RECEIVED",false),new AppointmentWorkDispatchMapper.Candidate(102L,"P-D",12L,"IP-D","D","RECEIVED",false))).when(mapper).candidates(batch.id());
|
||||||
doReturn(List.of(new AppointmentWorkDispatchMapper.Physical(1L,"B","FBA:ONT8",60,60))).when(mapper).physical(101L,"B");
|
doReturn(List.of(new AppointmentWorkDispatchMapper.Physical(1L,"B","FBA:ONT8",60,60))).when(mapper).physical(101L,"B");
|
||||||
doReturn(List.of(new AppointmentWorkDispatchMapper.Physical(2L,"D","FBA:ONT8",40,40))).when(mapper).physical(102L,"D");
|
doReturn(List.of(new AppointmentWorkDispatchMapper.Physical(2L,"D","FBA:ONT8",40,40))).when(mapper).physical(102L,"D");
|
||||||
|
|||||||
+14
-5
@@ -59,7 +59,13 @@ class AppointmentWorkDispatchPersistenceTest {
|
|||||||
var ds=new DriverManagerDataSource("jdbc:h2:mem:appointment-dispatch-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000","sa","");
|
var ds=new DriverManagerDataSource("jdbc:h2:mem:appointment-dispatch-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000","sa","");
|
||||||
jdbc=new JdbcTemplate(ds);var manager=new DataSourceTransactionManager(ds);tx=new TransactionTemplate(manager);tx.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
|
jdbc=new JdbcTemplate(ds);var manager=new DataSourceTransactionManager(ds);tx=new TransactionTemplate(manager);tx.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
|
||||||
jdbc.execute("CREATE TABLE ll_dispatch_order(id BIGINT PRIMARY KEY,dispatch_no VARCHAR(40),record_version BIGINT,status VARCHAR(30))");
|
jdbc.execute("CREATE TABLE ll_dispatch_order(id BIGINT PRIMARY KEY,dispatch_no VARCHAR(40),record_version BIGINT,status VARCHAR(30))");
|
||||||
jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT PRIMARY KEY,dispatch_order_id BIGINT,customer_id VARCHAR(40),plan_pallet_id BIGINT,source_stage VARCHAR(30),package_count INT)");
|
jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT PRIMARY KEY,dispatch_order_id BIGINT,customer_id VARCHAR(40),plan_pallet_id BIGINT,source_stage VARCHAR(30),package_count INT,destination_type VARCHAR(30),fba_warehouse_code VARCHAR(40))");
|
||||||
|
// 已收货来源读取库存余额,已拣来源独立读取扫码归属,不能退回计划数量代替。
|
||||||
|
jdbc.execute("CREATE TABLE ll_inventory_position(id BIGINT,customer_id VARCHAR(40),pallet_id BIGINT,balance_id BIGINT,available_packages INT,allocated_packages INT,picked_packages INT)");
|
||||||
|
jdbc.execute("CREATE TABLE ll_inventory_balance(id BIGINT,customer_id VARCHAR(40),inbound_code VARCHAR(128))");
|
||||||
|
jdbc.execute("CREATE TABLE ll_order(id BIGINT PRIMARY KEY,customer_id VARCHAR(40),inbound_code VARCHAR(128),destination_type VARCHAR(30),fba_warehouse_code VARCHAR(40),order_no VARCHAR(40))");
|
||||||
|
jdbc.execute("CREATE TABLE ll_dispatch_pda_pick(id BIGINT,customer_id VARCHAR(40),dispatch_item_id BIGINT)");
|
||||||
|
jdbc.execute("CREATE TABLE ll_dispatch_pda_pick_order(pick_id BIGINT,customer_id VARCHAR(40),order_id BIGINT,packages INT)");
|
||||||
jdbc.execute("CREATE TABLE ll_dispatch_appointment_allocation(dispatch_id BIGINT,dispatch_item_id BIGINT,plan_pallet_id BIGINT,batch_key VARCHAR(60),customer_id VARCHAR(40),source_key VARCHAR(40),destination_key VARCHAR(40),package_count INT)");
|
jdbc.execute("CREATE TABLE ll_dispatch_appointment_allocation(dispatch_id BIGINT,dispatch_item_id BIGINT,plan_pallet_id BIGINT,batch_key VARCHAR(60),customer_id VARCHAR(40),source_key VARCHAR(40),destination_key VARCHAR(40),package_count INT)");
|
||||||
jdbc.execute("CREATE TABLE ll_inbound_fba_appointment(id BIGINT PRIMARY KEY,appointment_no VARCHAR(40),version INT,replacement_keys_json VARCHAR(200))");
|
jdbc.execute("CREATE TABLE ll_inbound_fba_appointment(id BIGINT PRIMARY KEY,appointment_no VARCHAR(40),version INT,replacement_keys_json VARCHAR(200))");
|
||||||
jdbc.execute("CREATE TABLE ll_inbound_fba_appointment_item(appointment_id BIGINT,order_id BIGINT,customer_id VARCHAR(40),source_key VARCHAR(60),fba_warehouse_code VARCHAR(40),package_count INT,active INT)");
|
jdbc.execute("CREATE TABLE ll_inbound_fba_appointment_item(appointment_id BIGINT,order_id BIGINT,customer_id VARCHAR(40),source_key VARCHAR(60),fba_warehouse_code VARCHAR(40),package_count INT,active INT)");
|
||||||
@@ -97,7 +103,11 @@ class AppointmentWorkDispatchPersistenceTest {
|
|||||||
}
|
}
|
||||||
/** 插入真实客户、托盘和逐票数量,不从请求猜测。 */
|
/** 插入真实客户、托盘和逐票数量,不从请求猜测。 */
|
||||||
private void seed(long item,String customer,long order,int count,String destination) {
|
private void seed(long item,String customer,long order,int count,String destination) {
|
||||||
jdbc.update("INSERT INTO ll_dispatch_item VALUES(?,1,?,?,'RECEIVED',?)",item,customer,item+100,count);
|
jdbc.update("INSERT INTO ll_dispatch_item VALUES(?,1,?,?,'RECEIVED',?,?,'ONT8')",item,customer,item+100,count,destination);
|
||||||
|
jdbc.update("INSERT INTO ll_inventory_position VALUES(?,?,?,?,?,0,0)",item,customer,item+1000,item,count);
|
||||||
|
jdbc.update("INSERT INTO ll_inventory_balance VALUES(?,?,?)",item,customer,"IN-"+order);
|
||||||
|
if(jdbc.queryForObject("SELECT COUNT(*) FROM ll_order WHERE id=?",Integer.class,order)==0)
|
||||||
|
jdbc.update("INSERT INTO ll_order VALUES(?,?,?,?,'ONT8',?)",order,customer,"IN-"+order,destination,"WC-"+order);
|
||||||
jdbc.update("INSERT INTO ll_inbound_plan_pallet(id,pallet_no,plan_id,customer_id,warehouse_pallet_id) VALUES(?,?,1,?,?)",item+100,"P-"+item,customer,item+1000);
|
jdbc.update("INSERT INTO ll_inbound_plan_pallet(id,pallet_no,plan_id,customer_id,warehouse_pallet_id) VALUES(?,?,1,?,?)",item+100,"P-"+item,customer,item+1000);
|
||||||
jdbc.update("INSERT INTO ll_inbound_plan_line VALUES(?,?,?,?, 'ONT8')",item,order,customer,destination);
|
jdbc.update("INSERT INTO ll_inbound_plan_line VALUES(?,?,?,?, 'ONT8')",item,order,customer,destination);
|
||||||
jdbc.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(?,?,?,?,?)",item+100,item,customer,count,count);
|
jdbc.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(?,?,?,?,?)",item+100,item,customer,count,count);
|
||||||
@@ -163,7 +173,7 @@ class AppointmentWorkDispatchPersistenceTest {
|
|||||||
jdbc.update("UPDATE ll_inbound_fba_appointment SET version=3 WHERE id=1");assertEquals(3L,binding.candidateBatches(inputs).get(inputs.get(0)).appointmentBatches().get(0).recordVersion());
|
jdbc.update("UPDATE ll_inbound_fba_appointment SET version=3 WHERE id=1");assertEquals(3L,binding.candidateBatches(inputs).get(inputs.get(0)).appointmentBatches().get(0).recordVersion());
|
||||||
}
|
}
|
||||||
@Test void twoDispatchesCannotConsumeTheSameBatchQuotaConcurrently() throws Exception {
|
@Test void twoDispatchesCannotConsumeTheSameBatchQuotaConcurrently() throws Exception {
|
||||||
jdbc.update("INSERT INTO ll_dispatch_order VALUES(2,'D-2',0,'ACTIVE')");jdbc.update("INSERT INTO ll_dispatch_item VALUES(12,2,'101',111,'RECEIVED',80)");
|
jdbc.update("INSERT INTO ll_dispatch_order VALUES(2,'D-2',0,'ACTIVE')");jdbc.update("INSERT INTO ll_dispatch_item VALUES(12,2,'101',111,'RECEIVED',80,'FBA','ONT8')");
|
||||||
var start=new CyclicBarrier(2);var pool=Executors.newFixedThreadPool(2);
|
var start=new CyclicBarrier(2);var pool=Executors.newFixedThreadPool(2);
|
||||||
try {
|
try {
|
||||||
var results=new ArrayList<Future<Boolean>>();
|
var results=new ArrayList<Future<Boolean>>();
|
||||||
@@ -175,6 +185,7 @@ class AppointmentWorkDispatchPersistenceTest {
|
|||||||
@Test void lateConfirmedAppointmentCanBindBothExistingPalletsWithoutReplacingCargo() {
|
@Test void lateConfirmedAppointmentCanBindBothExistingPalletsWithoutReplacingCargo() {
|
||||||
jdbc.update("UPDATE ll_dispatch_item SET package_count=40 WHERE id=11");
|
jdbc.update("UPDATE ll_dispatch_item SET package_count=40 WHERE id=11");
|
||||||
jdbc.update("UPDATE ll_inbound_plan_pallet_allocation SET planned_package_count=40,counted_package_count=40 WHERE pallet_id=111");
|
jdbc.update("UPDATE ll_inbound_plan_pallet_allocation SET planned_package_count=40,counted_package_count=40 WHERE pallet_id=111");
|
||||||
|
jdbc.update("UPDATE ll_inventory_position SET available_packages=40 WHERE id=11");
|
||||||
seed(12,"101",1,40,"FBA");
|
seed(12,"101",1,40,"FBA");
|
||||||
var original=jdbc.queryForList("SELECT * FROM ll_dispatch_item ORDER BY id");
|
var original=jdbc.queryForList("SELECT * FROM ll_dispatch_item ORDER BY id");
|
||||||
tx.executeWithoutResult(s->bind(11,12));
|
tx.executeWithoutResult(s->bind(11,12));
|
||||||
@@ -201,8 +212,6 @@ class AppointmentWorkDispatchPersistenceTest {
|
|||||||
}
|
}
|
||||||
/** 查询本车重分配量排除本车原占用,保留其他车占用及真实业务标识。 */
|
/** 查询本车重分配量排除本车原占用,保留其他车占用及真实业务标识。 */
|
||||||
@Test void ownedAllocationOptionsExcludeOnlyThisDispatchAndKeepOtherCarsQuota() {
|
@Test void ownedAllocationOptionsExcludeOnlyThisDispatchAndKeepOtherCarsQuota() {
|
||||||
jdbc.execute("CREATE TABLE ll_order(id BIGINT PRIMARY KEY,customer_id VARCHAR(40),order_no VARCHAR(40),inbound_code VARCHAR(40))");
|
|
||||||
jdbc.update("INSERT INTO ll_order VALUES(1,'101','WC-1','IN-1'),(2,'202','WC-2','IN-2'),(3,'303','WC-3','IN-3')");
|
|
||||||
tx.executeWithoutResult(s->bind(11));
|
tx.executeWithoutResult(s->bind(11));
|
||||||
var identity=new CandidatePhysical("101",111L);
|
var identity=new CandidatePhysical("101",111L);
|
||||||
var option=binding.allocationOptions(1,List.of(identity)).get(identity).get(0);
|
var option=binding.allocationOptions(1,List.of(identity)).get(identity).get(0);
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ function server(fault = '') {
|
|||||||
pallets: [pallet()], missingFields: ['PLT-A:palletSpaces'] })
|
pallets: [pallet()], missingFields: ['PLT-A:palletSpaces'] })
|
||||||
}
|
}
|
||||||
if (method === 'PUT' && path === D) {
|
if (method === 'PUT' && path === D) {
|
||||||
assert.equal(body.publishLooseboardDemand, detail.publishLooseboardDemand)
|
assert.equal(Object.hasOwn(body, 'publishLooseboardDemand'), false)
|
||||||
if (detail.stage !== 'BEFORE_LOADING') {
|
if (detail.stage !== 'BEFORE_LOADING') {
|
||||||
if (fault === 'edit-mutates-on-rejection') detail.counts.pickedPackageCount--
|
if (fault === 'edit-mutates-on-rejection') detail.counts.pickedPackageCount--
|
||||||
return conflict('仅装车前可编辑,请显式退回;已经扫码不得改派')
|
return conflict('仅装车前可编辑,请显式退回;已经扫码不得改派')
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ export async function prepareFleetTrip(context, module, dispatchNo, warehouseId)
|
|||||||
maintenance.destinationAppointments = [...appointments.values()]
|
maintenance.destinationAppointments = [...appointments.values()]
|
||||||
proof.updateRequest = { recordVersion: before.recordVersion, idempotencyKey: key(proof, 'EDIT'),
|
proof.updateRequest = { recordVersion: before.recordVersion, idempotencyKey: key(proof, 'EDIT'),
|
||||||
maintenance,
|
maintenance,
|
||||||
publishLooseboardDemand: before.publishLooseboardDemand,
|
|
||||||
vehicle: { mode: 'MASTER', vehicleId: proof.vehicleId, evidenceFileIds: [proof.reviewPhotos.VEHICLE.fileId] },
|
vehicle: { mode: 'MASTER', vehicleId: proof.vehicleId, evidenceFileIds: [proof.reviewPhotos.VEHICLE.fileId] },
|
||||||
driver: { mode: 'MASTER', driverId: proof.driverId, evidence: { expectedTaskEndAt: proof.expectedDriverEndAt,
|
driver: { mode: 'MASTER', driverId: proof.driverId, evidence: { expectedTaskEndAt: proof.expectedDriverEndAt,
|
||||||
fileIds: [proof.reviewPhotos.DRIVER.fileId], photoFileIds: [proof.reviewPhotos.DRIVER_PHOTO.fileId] } },
|
fileIds: [proof.reviewPhotos.DRIVER.fileId], photoFileIds: [proof.reviewPhotos.DRIVER_PHOTO.fileId] } },
|
||||||
|
|||||||
Reference in New Issue
Block a user