fix(firstleg): settle and validate actual partial loading cargo

This commit is contained in:
2026-10-04 19:01:40 +08:00
parent 22133fe604
commit 7319fba35a
16 changed files with 936 additions and 49 deletions
@@ -8,6 +8,16 @@ import java.util.List;
@Mapper
public interface ContainerCustomsMapper {
/** Same settled loading-item scope as the V3 handover; retained skipped rows are not customs cargo. */
String ACTUAL_ITEM_SCOPE = """
EXISTS (SELECT 1 FROM fl_loading_pick_item actual_pick
JOIN fl_loading_pick_task actual_task ON actual_task.id=actual_pick.pick_task_id
WHERE actual_task.loading_plan_id=item.loading_plan_id
AND actual_pick.loading_plan_item_id=item.id
AND actual_pick.item_status IN ('PICKED','PARTIAL')
AND actual_pick.picked_package_count > 0
AND actual_pick.picked_package_count=item.package_count)
""";
String DOCUMENT_COLUMNS = """
d.id id,d.customs_container_id customsContainerId,d.tenant_id tenantId,d.file_id fileId,
d.file_name fileName,d.file_hash fileHash,d.content_type contentType,d.size_bytes sizeBytes,
@@ -36,6 +46,9 @@ public interface ContainerCustomsMapper {
JOIN fl_loading_plan_item item ON item.loading_plan_id=p.id AND item.deleted=0
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE p.id=#{planId}
AND (p.plan_status != 'COMPLETED' OR
""" + ACTUAL_ITEM_SCOPE + """
)
ORDER BY i.tenant_id,i.id
""")
List<InboundRow> planInbounds(@Param("planId") long planId);
@@ -46,6 +59,8 @@ public interface ContainerCustomsMapper {
JOIN fl_loading_plan p ON p.id=item.loading_plan_id AND p.plan_status='COMPLETED'
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE item.loading_plan_id=#{planId} AND item.deleted=0
AND
""" + ACTUAL_ITEM_SCOPE + """
AND (#{tenantId} IS NULL OR i.tenant_id=#{tenantId})
AND (#{taxMode} IS NULL OR i.tax_mode=#{taxMode})
""")
@@ -66,6 +81,8 @@ public interface ContainerCustomsMapper {
JOIN fl_loading_plan_item item ON item.loading_plan_id=p.id AND item.deleted=0
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE p.plan_status='COMPLETED' AND i.tenant_id=#{tenantId} AND i.order_id=#{orderId} AND i.tax_mode='TAX_REFUND'
AND
""" + ACTUAL_ITEM_SCOPE + """
ORDER BY COALESCE(p.confirmed_at,p.updated_at) DESC,p.id DESC LIMIT 1
""")
Long completedPlanForOrder(@Param("tenantId") long tenantId, @Param("orderId") long orderId);
@@ -325,6 +342,8 @@ public interface ContainerCustomsMapper {
SELECT DISTINCT i.id FROM fl_loading_plan_item item
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE item.loading_plan_id=#{planId} AND item.deleted=0 AND i.tax_mode='TAX_REFUND'
AND
""" + ACTUAL_ITEM_SCOPE + """
) target LEFT JOIN fl_customs_customer_document_inbound s ON s.inbound_id=target.id
LEFT JOIN fl_customs_customer_document d ON d.id=s.document_id AND d.customs_container_id=#{containerId} AND d.status='ACTIVE'
WHERE d.id IS NULL
@@ -336,6 +355,8 @@ public interface ContainerCustomsMapper {
FROM fl_loading_plan_item item
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE item.loading_plan_id=#{planId} AND item.deleted=0 AND i.tax_mode='TAX_REFUND'
AND
""" + ACTUAL_ITEM_SCOPE + """
""")
int taxRefundCount(@Param("planId") long planId);
@@ -541,6 +562,8 @@ public interface ContainerCustomsMapper {
LEFT JOIN fl_outbound_dispatch d ON d.inbound_id=item.inbound_id
AND d.tenant_id=item.account_id AND d.container_booking_id=c.container_booking_id
WHERE item.loading_plan_id=c.loading_plan_id AND item.deleted=0
AND
""" + ACTUAL_ITEM_SCOPE + """
GROUP BY item.loading_plan_id HAVING COUNT(*)=SUM(CASE WHEN d.status='WAITING_FOR_LOADING' THEN 1 ELSE 0 END)
) ORDER BY c.completed_at,c.id LIMIT #{limit}
""")
@@ -557,6 +580,8 @@ public interface ContainerCustomsMapper {
JOIN fl_container_booking b ON b.id=p.container_booking_id AND b.record_status='ACTIVE'
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE p.id=#{planId} AND item.deleted=0 AND i.tax_mode='BUY_DECLARATION'
AND
""" + ACTUAL_ITEM_SCOPE + """
AND item.id>#{cursor} AND item.id<=#{maximumId}
ORDER BY item.id LIMIT #{limit}
""")
@@ -575,6 +600,8 @@ public interface ContainerCustomsMapper {
JOIN fl_loading_plan p ON p.id=item.loading_plan_id AND p.plan_status='COMPLETED'
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE p.id=#{planId} AND item.deleted=0 AND i.tax_mode='BUY_DECLARATION'
AND
""" + ACTUAL_ITEM_SCOPE + """
""")
long maximumBuyItemId(@Param("planId") long planId);
@@ -583,6 +610,8 @@ public interface ContainerCustomsMapper {
JOIN fl_loading_plan p ON p.id=item.loading_plan_id AND p.plan_status='COMPLETED'
JOIN fl_inbound_order i ON i.id=item.inbound_id AND i.tenant_id=item.account_id AND i.deleted=0
WHERE p.id=#{planId} AND item.deleted=0 AND i.tax_mode='BUY_DECLARATION' AND item.id<=#{maximumId}
AND
""" + ACTUAL_ITEM_SCOPE + """
""")
long countBuyRows(@Param("planId") long planId,@Param("maximumId") long maximumId);
@@ -250,14 +250,29 @@ public interface InboundSplitSettlementMapper {
@Update("""
UPDATE fl_loading_plan_item plan_item
JOIN fl_loading_pick_item pick_item ON pick_item.loading_plan_item_id = plan_item.id
SET plan_item.pallet_count = CEIL(COALESCE(plan_item.pallet_count,0)
* pick_item.picked_package_count / NULLIF(plan_item.package_count,0)),
SET plan_item.pallet_count = CEIL(CAST(COALESCE(plan_item.pallet_count,0) AS DECIMAL(30,6))
* pick_item.picked_package_count / NULLIF(pick_item.planned_package_count,0)),
plan_item.product_quantity = CASE WHEN plan_item.units_per_carton IS NOT NULL
THEN plan_item.units_per_carton * pick_item.picked_package_count
ELSE ROUND(CAST(plan_item.product_quantity AS DECIMAL(30,6)) * pick_item.picked_package_count
/ NULLIF(pick_item.planned_package_count,0),0) END,
plan_item.gross_weight_kg = ROUND(plan_item.gross_weight_kg * pick_item.picked_package_count
/ NULLIF(pick_item.planned_package_count,0),3),
plan_item.net_weight_kg = ROUND(plan_item.net_weight_kg * pick_item.picked_package_count
/ NULLIF(pick_item.planned_package_count,0),3),
plan_item.dimensional_weight_kg = ROUND(plan_item.dimensional_weight_kg * pick_item.picked_package_count
/ NULLIF(pick_item.planned_package_count,0),3),
plan_item.volume_cbm = ROUND(plan_item.volume_cbm * pick_item.picked_package_count
/ NULLIF(pick_item.planned_package_count,0),4),
plan_item.package_count = pick_item.picked_package_count,
plan_item.updated_by = #{operatorId}, plan_item.updated_by_name = #{operatorName},
plan_item.updated_at = CURRENT_TIMESTAMP(3), plan_item.version = plan_item.version + 1
WHERE plan_item.id = #{planItemId} AND plan_item.deleted = 0
AND plan_item.unit_type = 'PACKAGE' AND plan_item.pallet_id IS NULL
AND pick_item.item_status = 'PARTIAL'
AND pick_item.picked_package_count > 0
AND pick_item.picked_package_count < pick_item.planned_package_count
AND plan_item.package_count = pick_item.planned_package_count
""")
int convergePartialPlanItem(@Param("planItemId") long planItemId, @Param("operatorId") long operatorId,
@Param("operatorName") String operatorName);
@@ -13,7 +13,7 @@ import java.util.List;
/** 读取 PDA 结算后的最终实装入仓单和冻结货物快照。 */
@Mapper
public interface LoadingCompletedEventMapper {
@Select("""
String SNAPSHOT_ROWS = """
SELECT p.id AS loading_plan_id, p.plan_no, p.container_booking_id, p.version AS aggregate_version,
b.container_no, b.cabinet_no, b.warehouse_id, b.warehouse_name,
i.customer_name,
@@ -29,7 +29,10 @@ public interface LoadingCompletedEventMapper {
i.product_name_zh, i.product_name_en, i.package_count, i.product_quantity,
i.pallet_count, i.gross_weight_kg, i.net_weight_kg, i.dimensional_weight_kg,
i.volume_cbm, i.fba_tracking_no, i.shipment_no, i.shipping_mark
,i.customer_remark, i.internal_remark, i.warehouse_remark
,i.customer_remark, i.internal_remark, i.warehouse_remark,
i.units_per_carton, i.unit_type, i.pallet_id,
actual_pick.planned_package_count, actual_pick.picked_package_count,
actual_pick.item_status AS pick_status
FROM fl_loading_plan p
JOIN fl_container_booking b ON b.id = p.container_booking_id
JOIN fl_loading_plan_item i ON i.loading_plan_id = p.id AND i.deleted = 0
@@ -37,17 +40,33 @@ public interface LoadingCompletedEventMapper {
ON actual_pick.loading_plan_item_id = i.id
AND actual_pick.item_status IN ('PICKED','PARTIAL')
AND actual_pick.picked_package_count > 0
AND actual_pick.picked_package_count = i.package_count
JOIN fl_loading_pick_task actual_task
ON actual_task.id = actual_pick.pick_task_id
AND actual_task.loading_plan_id = p.id
JOIN fl_inbound_order o ON o.id = i.inbound_id AND o.tenant_id = i.account_id AND o.deleted = 0
LEFT JOIN fl_inbound_order root_order ON root_order.id = COALESCE(o.root_inbound_id, o.id)
WHERE p.id = #{planId} AND i.package_count > 0
ORDER BY i.account_id, i.inbound_id, i.inbound_detail_id, i.id
""")
""";
String SNAPSHOT_ORDER = " ORDER BY i.account_id, i.inbound_id, i.inbound_detail_id, i.id";
@Select(SNAPSHOT_ROWS + " AND actual_pick.picked_package_count = i.package_count" + SNAPSHOT_ORDER)
List<Row> rows(@Param("planId") long planId);
/** 尚未结算的 PACKAGE/PARTIAL 也必须接受整柜预检;这里只读原始冻结行。 */
@Select(SNAPSHOT_ROWS + SNAPSHOT_ORDER)
List<Row> previewRows(@Param("planId") long planId);
@Select("""
SELECT COUNT(*) FROM fl_loading_plan_item i
JOIN fl_loading_pick_item actual_pick ON actual_pick.loading_plan_item_id=i.id
JOIN fl_loading_pick_task actual_task ON actual_task.id=actual_pick.pick_task_id
AND actual_task.loading_plan_id=i.loading_plan_id
WHERE i.loading_plan_id=#{planId} AND i.deleted=0
AND actual_pick.item_status IN ('PICKED','PARTIAL') AND actual_pick.picked_package_count>0
AND actual_pick.picked_package_count<>i.package_count
""")
int unsettledActualItems(@Param("planId") long planId);
/** 读取头程托盘及箱唛快照;普通订单向尾程交接打印编号,一键代发同时冻结托盘库存。 */
@Select("""
SELECT i.account_id, i.inbound_id AS source_inbound_id,
@@ -139,6 +158,8 @@ public interface LoadingCompletedEventMapper {
private String fbaTrackingNo; private String shipmentNo; private String shippingMark;
private String businessType; private String fulfillmentType; private String lxCode; private String destinationType;
private String customerRemark; private String internalRemark; private String warehouseRemark;
private Integer unitsPerCarton; private String unitType; private Long palletId;
private Integer plannedPackageCount; private Integer pickedPackageCount; private String pickStatus;
}
@Getter @Setter
@@ -18,7 +18,7 @@ public interface LoadingPalletLabelMapper {
i.id AS item_id,i.account_id,i.inbound_id,i.inbound_code,i.destination_warehouse_id,
i.destination_warehouse_code,i.destination_warehouse_name,
COALESCE(NULLIF(i.pallet_count,0),
CEIL(COALESCE(NULLIF(o.pallet_count,0),
CEIL(CAST(COALESCE(NULLIF(o.pallet_count,0),
NULLIF((SELECT COUNT(DISTINCT weight.id)
FROM fl_inbound_pallet_allocation allocation
JOIN fl_inbound_weight_result weight
@@ -31,7 +31,7 @@ public interface LoadingPalletLabelMapper {
AND allocation.allocation_status IN ('CONFIRMED','TRANSFERRED','RELEASED')),0),
(SELECT COUNT(*) FROM fl_inbound_weight_result weight
WHERE weight.tenant_id=i.account_id AND weight.inbound_id=i.inbound_id
AND weight.deleted=0 AND weight.recognition_status='CONFIRMED'),0)
AND weight.deleted=0 AND weight.recognition_status='CONFIRMED'),0) AS DECIMAL(30,6))
* i.package_count / NULLIF(o.total_package_count,0)),0) AS pallet_count,
COALESCE(o.fulfillment_type,'STANDARD') AS fulfillment_type,
o.destination_type,i.load_sequence
@@ -45,6 +45,44 @@ public interface LoadingPalletLabelMapper {
""")
List<SourceRow> lockSources(@Param("planId") long planId);
/** 预测结算后的来源范围和托数;不锁行、不生成或占用标签。 */
@Select("""
SELECT p.id AS plan_id,p.plan_no,p.plan_status,b.container_no,b.cabinet_no,b.warehouse_id,
i.id AS item_id,i.account_id,i.inbound_id,i.inbound_code,i.destination_warehouse_id,
i.destination_warehouse_code,i.destination_warehouse_name,
COALESCE(NULLIF(CASE WHEN pick.picked_package_count<i.package_count
THEN CEIL(CAST(COALESCE(i.pallet_count,0) AS DECIMAL(30,6))
* pick.picked_package_count/NULLIF(pick.planned_package_count,0))
ELSE i.pallet_count END,0),
CEIL(CAST(COALESCE(
NULLIF((SELECT COUNT(DISTINCT weight.id)
FROM fl_inbound_pallet_allocation allocation
JOIN fl_inbound_weight_result weight
ON weight.id=allocation.weight_result_id AND weight.tenant_id=allocation.tenant_id
AND weight.deleted=0 AND weight.recognition_status='CONFIRMED'
AND (weight.inbound_id=o.id OR weight.inbound_id=o.root_inbound_id)
WHERE allocation.tenant_id=i.account_id AND allocation.inbound_id=i.inbound_id
AND allocation.block_id>0 AND allocation.slot_no>0 AND allocation.reservation_id>0
AND allocation.allocated_package_count>0
AND allocation.allocation_status IN ('CONFIRMED','TRANSFERRED','RELEASED')),0),
(SELECT COUNT(*) FROM fl_inbound_weight_result weight
WHERE weight.tenant_id=i.account_id AND weight.inbound_id=i.inbound_id
AND weight.deleted=0 AND weight.recognition_status='CONFIRMED'),0) AS DECIMAL(30,6))
* pick.picked_package_count / NULLIF(o.total_package_count,0)),0) AS pallet_count,
COALESCE(o.fulfillment_type,'STANDARD') AS fulfillment_type,
o.destination_type,i.load_sequence
FROM fl_loading_plan p
JOIN fl_container_booking b ON b.id=p.container_booking_id AND b.record_status='ACTIVE'
JOIN fl_loading_plan_item i ON i.loading_plan_id=p.id AND i.deleted=0
JOIN fl_loading_pick_item pick ON pick.loading_plan_item_id=i.id
AND pick.item_status IN ('PICKED','PARTIAL') AND pick.picked_package_count>0
JOIN fl_loading_pick_task task ON task.id=pick.pick_task_id AND task.loading_plan_id=p.id
LEFT JOIN fl_inbound_order o ON o.id=i.inbound_id AND o.tenant_id=i.account_id AND o.deleted=0
WHERE p.id=#{planId}
ORDER BY i.destination_warehouse_code,i.load_sequence,i.id
""")
List<SourceRow> previewSources(@Param("planId") long planId);
@Select("""
SELECT id AS label_id,loading_plan_id AS plan_id,loading_plan_item_id AS item_id,
account_id,inbound_id,inbound_code,container_no,destination_warehouse_code,
@@ -28,6 +28,7 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -53,24 +54,33 @@ public class LoadingCompletedOutboxService {
@Resource private ObjectMapper objectMapper;
public void publish(long planId) {
preparedEnvelopes(planId).forEach(this::insert);
if (snapshotMapper.unsettledActualItems(planId) != 0) {
throw new BizException(ResultCode.CONFLICT, "实装明细尚未完成结算,不能发布尾程交接事件");
}
preparedEnvelopes(planId, false).forEach(this::insert);
}
/** 在任何入库结算前验证跨区交接快照,避免整托/箱唛冲突留下半完成状态。 */
public void requirePublishable(long planId) {
preparedEnvelopes(planId);
preparedEnvelopes(planId, true);
}
private List<EventEnvelope> preparedEnvelopes(long planId) {
List<Row> rows = snapshotMapper.rows(planId);
private List<EventEnvelope> preparedEnvelopes(long planId, boolean preview) {
List<Row> rows = preview ? snapshotMapper.previewRows(planId) : snapshotMapper.rows(planId);
if (rows.isEmpty()) throw new BizException(ResultCode.CONFLICT, "装柜计划没有最终实装入仓单,不能生成尾程交接事件");
if (preview) rows.forEach(LoadingCompletedOutboxService::projectSettledRow);
validateContainerIdentity(rows);
validateCustomerSnapshots(rows);
palletLabelService.prepare(planId, rows.get(0).getWarehouseId());
List<com.baiyue.firstleg.mapper.LoadingPalletLabelMapper.LabelRow> standardLabels;
if (preview) {
standardLabels = palletLabelService.preview(planId, rows.get(0).getWarehouseId());
} else {
palletLabelService.prepare(planId, rows.get(0).getWarehouseId());
standardLabels = palletLabelMapper.labels(planId);
}
Map<Long, List<String>> cartonMarks = cartonLabelPools(rows);
List<PalletRow> palletRows = palletRows(planId);
Map<String, PalletManifest> palletManifests = palletManifests(palletRows);
List<com.baiyue.firstleg.mapper.LoadingPalletLabelMapper.LabelRow> standardLabels = palletLabelMapper.labels(planId);
return rows.stream().collect(java.util.stream.Collectors.groupingBy(Row::getAccountId,
java.util.TreeMap::new, java.util.stream.Collectors.toList()))
.entrySet().stream().map(entry -> envelope(entry.getKey(), entry.getValue(),
@@ -80,6 +90,37 @@ public class LoadingCompletedOutboxService {
.toList();
}
/** 与 convergePartialPlanItem 相同的冻结分配口径;已结算的行绝不再次缩放。 */
static void projectSettledRow(Row row) {
Integer actual = row.getPickedPackageCount();
Integer planned = row.getPlannedPackageCount();
Integer frozen = row.getPackageCount();
if (actual == null || actual <= 0 || planned == null || planned < actual) {
throw new BizException(ResultCode.CONFLICT, "装柜实拣数量与计划快照不一致");
}
if (actual.equals(frozen)) return;
if (!"PARTIAL".equals(row.getPickStatus()) || !"PACKAGE".equals(row.getUnitType())
|| row.getPalletId() != null || !planned.equals(frozen) || actual >= planned) {
throw new BizException(ResultCode.CONFLICT, "部分装柜尚未形成合法的冻结分配快照");
}
Integer units = row.getUnitsPerCarton();
row.setProductQuantity(units == null
? (row.getProductQuantity() == null ? null : scaled(BigDecimal.valueOf(row.getProductQuantity()), actual, planned, 0).intValueExact())
: Math.multiplyExact(units, actual));
row.setPalletCount(BigDecimal.valueOf(Objects.requireNonNullElse(row.getPalletCount(), 0))
.multiply(BigDecimal.valueOf(actual)).divide(BigDecimal.valueOf(planned), 0, RoundingMode.CEILING).intValueExact());
row.setGrossWeightKg(scaled(row.getGrossWeightKg(), actual, planned, 3));
row.setNetWeightKg(scaled(row.getNetWeightKg(), actual, planned, 3));
row.setDimensionalWeightKg(scaled(row.getDimensionalWeightKg(), actual, planned, 3));
row.setVolumeCbm(scaled(row.getVolumeCbm(), actual, planned, 4));
row.setPackageCount(actual);
}
private static BigDecimal scaled(BigDecimal value, int actual, int planned, int scale) {
return value == null ? null : value.multiply(BigDecimal.valueOf(actual))
.divide(BigDecimal.valueOf(planned), scale, RoundingMode.HALF_UP);
}
/** 已开展逐箱打托的入仓单使用现场事实;未开展的存量入仓单继续兼容原托盘分配快照。 */
private List<PalletRow> palletRows(long planId) {
int workPalletCount = snapshotMapper.workPalletCount(planId);
@@ -47,6 +47,28 @@ public class LoadingPalletLabelService {
@Transactional
public PalletLabelManifest prepare(long planId, long warehouseId) {
List<SourceRow> sources = mapper.lockSources(planId);
List<LabelRow> expected = expectedSources(sources, warehouseId);
List<LabelRow> existing = mapper.labels(planId);
if (existing.isEmpty()) {
expected.forEach(row -> {
row.setLabelId(IdWorker.getId());
if (mapper.insert(row) != 1) throw new BizException(ResultCode.CONFLICT, "托盘码生成失败,请刷新后重试");
});
existing = mapper.labels(planId);
}
requireSame(expected, existing);
return manifest(sources.get(0), existing);
}
/** 结算前验证预测标签,仅读取;不分配持久标识、锁行或新增标签。 */
public List<LabelRow> preview(long planId, long warehouseId) {
List<LabelRow> expected = expectedSources(mapper.previewSources(planId), warehouseId);
List<LabelRow> existing = mapper.labels(planId);
if (!existing.isEmpty()) requireSame(expected, existing);
return existing.isEmpty() ? expected : existing;
}
private List<LabelRow> expectedSources(List<SourceRow> sources, long warehouseId) {
if (sources.isEmpty()) throw new BizException(ResultCode.NOT_FOUND, "装柜计划不存在或没有有效明细");
SourceRow first = sources.get(0);
if (!Objects.equals(first.getWarehouseId(), warehouseId)) throw new BizException(ResultCode.NOT_FOUND, "装柜计划不属于当前仓库");
@@ -64,14 +86,7 @@ public class LoadingPalletLabelService {
throw new BizException(ResultCode.CONFLICT, "散货明细缺少 FBA 仓信息,不能生成托盘码");
}
Map<Long, String> internalCodes = resolveInternalCodes(standard);
List<LabelRow> existing = mapper.labels(planId);
List<LabelRow> expected = expected(first, standard, containerNo, internalCodes);
if (existing.isEmpty()) {
expected.forEach(row -> { if (mapper.insert(row) != 1) throw new BizException(ResultCode.CONFLICT, "托盘码生成失败,请刷新后重试"); });
existing = mapper.labels(planId);
}
requireSame(expected, existing);
return manifest(first, existing);
return expected(first, standard, containerNo, internalCodes);
}
@Transactional
@@ -103,7 +118,7 @@ public class LoadingPalletLabelService {
for (int local = 0; local < source.getPalletCount(); local++) {
int index = indexes.merge(source.getDestinationWarehouseId(), 1, Integer::sum);
if (index > 999) throw new BizException(ResultCode.CONFLICT, "单柜同一 FBA 仓托盘数不能超过 999");
LabelRow row = new LabelRow(); row.setLabelId(IdWorker.getId()); row.setPlanId(plan.getPlanId());
LabelRow row = new LabelRow(); row.setPlanId(plan.getPlanId());
row.setItemId(source.getItemId()); row.setAccountId(source.getAccountId()); row.setInboundId(source.getInboundId());
row.setInboundCode(source.getInboundCode()); row.setContainerNo(containerNo);
row.setDestinationWarehouseCode(warehouseCode); row.setDestinationWarehouseName(source.getDestinationWarehouseName());