贴标申请,仓储转运

This commit is contained in:
2026-09-24 09:01:14 +08:00
parent 10a2ab3e42
commit 186b6be7d8
27 changed files with 1203 additions and 118 deletions
@@ -60,7 +60,7 @@ public interface DispatchCandidateMapper {
"GROUP_CONCAT(DISTINCT o.order_no ORDER BY o.order_no SEPARATOR ',') AS orderRefs,MIN(l.inbound_code) AS inboundCode," +
"p.pallet_no AS palletNo,p.destination_warehouse_id AS destinationWarehouseId," +
"p.destination_warehouse_code AS destinationWarehouseCode,p.destination_warehouse_name AS destinationWarehouseName," +
"MIN(l.fba_warehouse_code) AS fbaWarehouseCode," +
"COALESCE(NULLIF(MIN(l.fba_warehouse_code),''),NULLIF(p.destination_warehouse_code,'')) AS fbaWarehouseCode," +
"CASE WHEN p.warehouse_pallet_id IS NOT NULL THEN COALESCE((SELECT SUM(pos.available_packages) " +
"FROM ll_inventory_position pos WHERE pos.customer_id=ip.customer_id AND pos.pallet_id=p.warehouse_pallet_id),0) " +
"ELSE COALESCE(SUM(CASE WHEN a.picked_package_count > 0 THEN a.picked_package_count " +
@@ -79,15 +79,20 @@ public interface DispatchCandidateMapper {
"WHERE ri.customer_id=ip.customer_id AND rp.pallet_id=p.warehouse_pallet_id " +
"AND ri.fba_carton_mark IS NOT NULL AND ri.fba_carton_mark != '' " +
"AND ra.status NOT IN ('REJECTED','CANCELLED')) AS relabelCartonMarks," +
"MIN(l.destination_type) AS destinationType,MIN(l.delivery_address) AS deliveryAddress," +
"COALESCE(NULLIF(MIN(l.destination_type),''),NULLIF(p.destination_type,'')) AS destinationType,COALESCE(MIN(l.delivery_address),p.delivery_address) AS deliveryAddress," +
"MIN(l.postal_code) AS postalCode,MIN(l.contact_name) AS contactName,MIN(l.contact_phone) AS contactPhone " +
"FROM ll_inbound_plan_pallet p JOIN ll_inbound_plan ip ON ip.id=p.plan_id " +
"LEFT JOIN ll_warehouse_transfer_order wt ON wt.customer_id=ip.customer_id AND wt.lastleg_order_id=ip.source_order_id " +
"LEFT JOIN ll_inbound_plan_pallet_allocation a ON a.pallet_id=p.id " +
"LEFT JOIN ll_inbound_plan_line l ON l.id=a.line_id " +
"LEFT JOIN ll_order o ON o.id=l.order_id " +
"WHERE ((l.destination_type='FBA' AND NULLIF(TRIM(l.fba_warehouse_code),'') IS NOT NULL) OR " +
"WHERE ((ip.creation_mode='WAREHOUSE_TRANSFER' AND ((wt.destination_type='FBA' AND NULLIF(TRIM(wt.destination_warehouse_code),'') IS NOT NULL) OR " +
"(wt.destination_type='PRIVATE' AND NULLIF(TRIM(wt.private_destination_address),'') IS NOT NULL) OR " +
"(p.destination_type='FBA' AND NULLIF(TRIM(p.destination_warehouse_code),'') IS NOT NULL) OR " +
"(p.destination_type='PRIVATE' AND NULLIF(TRIM(p.delivery_address),'') IS NOT NULL))) OR " +
"(ip.creation_mode!='WAREHOUSE_TRANSFER' AND ((l.destination_type='FBA' AND NULLIF(TRIM(l.fba_warehouse_code),'') IS NOT NULL) OR " +
"(l.destination_type='PRIVATE' AND NULLIF(TRIM(l.delivery_address),'') IS NOT NULL AND NULLIF(TRIM(l.postal_code),'') IS NOT NULL " +
"AND NULLIF(TRIM(l.contact_name),'') IS NOT NULL AND NULLIF(TRIM(l.contact_phone),'') IS NOT NULL)) " +
"AND NULLIF(TRIM(l.contact_name),'') IS NOT NULL AND NULLIF(TRIM(l.contact_phone),'') IS NOT NULL)))) " +
"<if test='customerId != null and customerId != \"\"'> AND ip.customer_id=#{customerId}</if> " +
"<if test='planIds != null and planIds.size() > 0'> AND p.plan_id IN " +
"<foreach collection='planIds' item='id' open='(' separator=',' close=')'>#{id}</foreach></if> " +
@@ -107,12 +112,18 @@ public interface DispatchCandidateMapper {
"d.source_pallet_key=CONCAT('PLAN:',p.id) OR (p.warehouse_pallet_id IS NOT NULL AND " +
"d.source_pallet_key=CONCAT('RECEIVED:',p.warehouse_pallet_id))) " +
"AND (#{draftToken} IS NULL OR d.draft_token IS NULL OR d.draft_token != #{draftToken})) " +
"AND NOT EXISTS (SELECT 1 FROM ll_warehouse_transfer_inventory_allocation ta " +
// Warehouse-transfer inventory is intentionally reserved before dispatch. It must still be
// eligible for the normal smart-dispatch flow once its destination is bound; other order
// types keep the reservation guard to prevent duplicate dispatching.
"AND (ip.creation_mode='WAREHOUSE_TRANSFER' OR NOT EXISTS (SELECT 1 FROM ll_warehouse_transfer_inventory_allocation ta " +
"JOIN ll_inventory_position tp ON tp.id=ta.inventory_position_id " +
"WHERE ta.customer_id=ip.customer_id AND ta.status='RESERVED' AND tp.pallet_id=p.warehouse_pallet_id) " +
"AND NOT EXISTS (SELECT 1 FROM ll_relabel_item rli JOIN ll_relabel_application rla ON rla.id=rli.application_id AND rla.customer_id=rli.customer_id " +
"WHERE ta.customer_id=ip.customer_id AND ta.status='RESERVED' AND tp.pallet_id=p.warehouse_pallet_id)) " +
// A warehouse-transfer pallet may contain both relabel and non-relabel inventory.
// Do not hide the whole pallet because one carton/SKU has an active relabel item;
// the remaining available inventory must still be dispatchable.
"AND (ip.creation_mode!='WAREHOUSE_TRANSFER' OR NOT EXISTS (SELECT 1 FROM ll_relabel_item rli JOIN ll_relabel_application rla ON rla.id=rli.application_id AND rla.customer_id=rli.customer_id " +
"WHERE rli.customer_id=ip.customer_id AND rli.source_inbound_order_id IS NOT NULL " +
"AND rli.source_inbound_order_id=o.id AND rla.status NOT IN ('REJECTED','CANCELLED','COMPLETED')) " +
"AND rli.source_inbound_order_id=o.id AND rla.status NOT IN ('REJECTED','CANCELLED','COMPLETED'))) " +
"GROUP BY p.plan_id,p.id,p.warehouse_pallet_id,p.pallet_no,p.destination_warehouse_id," +
"p.destination_warehouse_code,p.destination_warehouse_name,p.allocated_volume_cbm " +
"ORDER BY p.plan_id,p.sequence_no" +
@@ -160,7 +171,7 @@ public interface DispatchCandidateMapper {
"GROUP_CONCAT(DISTINCT o.order_no ORDER BY o.order_no SEPARATOR ',') AS orderRefs,MIN(l.inbound_code) AS inboundCode," +
"p.pallet_no AS palletNo,p.destination_warehouse_id AS destinationWarehouseId," +
"p.destination_warehouse_code AS destinationWarehouseCode,p.destination_warehouse_name AS destinationWarehouseName," +
"MIN(l.fba_warehouse_code) AS fbaWarehouseCode," +
"COALESCE(NULLIF(MIN(l.fba_warehouse_code),''),NULLIF(p.destination_warehouse_code,'')) AS fbaWarehouseCode," +
"CASE WHEN p.warehouse_pallet_id IS NOT NULL THEN COALESCE((SELECT SUM(pos.available_packages) " +
"FROM ll_inventory_position pos WHERE pos.customer_id=#{customerId} AND pos.pallet_id=p.warehouse_pallet_id),0) " +
"ELSE COALESCE(SUM(CASE WHEN a.picked_package_count &gt; 0 THEN a.picked_package_count " +
@@ -179,31 +190,36 @@ public interface DispatchCandidateMapper {
"WHERE ri.customer_id=ip.customer_id AND rp.pallet_id=p.warehouse_pallet_id " +
"AND ri.fba_carton_mark IS NOT NULL AND ri.fba_carton_mark != '' " +
"AND ra.status NOT IN ('REJECTED','CANCELLED')) AS relabelCartonMarks," +
"MIN(l.destination_type) AS destinationType,MIN(l.delivery_address) AS deliveryAddress," +
"COALESCE(NULLIF(MIN(l.destination_type),''),NULLIF(p.destination_type,'')) AS destinationType,COALESCE(MIN(l.delivery_address),p.delivery_address) AS deliveryAddress," +
"MIN(l.postal_code) AS postalCode,MIN(l.contact_name) AS contactName,MIN(l.contact_phone) AS contactPhone " +
"FROM ll_inbound_plan_pallet p JOIN ll_inbound_plan ip ON ip.id=p.plan_id " +
"LEFT JOIN ll_warehouse_transfer_order wt ON wt.customer_id=ip.customer_id AND wt.lastleg_order_id=ip.source_order_id " +
"LEFT JOIN ll_inbound_plan_pallet_allocation a ON a.pallet_id=p.id " +
"LEFT JOIN ll_inbound_plan_line l ON l.id=a.line_id " +
"LEFT JOIN ll_order o ON o.id=l.order_id " +
"WHERE ip.customer_id=#{customerId} AND p.id IN " +
"<foreach collection='planPalletIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> " +
"AND ((l.destination_type='FBA' AND NULLIF(TRIM(l.fba_warehouse_code),'') IS NOT NULL) OR " +
"AND ((ip.creation_mode='WAREHOUSE_TRANSFER' AND ((wt.destination_type='FBA' AND NULLIF(TRIM(wt.destination_warehouse_code),'') IS NOT NULL) OR " +
"(wt.destination_type='PRIVATE' AND NULLIF(TRIM(wt.private_destination_address),'') IS NOT NULL) OR " +
"(p.destination_type='FBA' AND NULLIF(TRIM(p.destination_warehouse_code),'') IS NOT NULL) OR " +
"(p.destination_type='PRIVATE' AND NULLIF(TRIM(p.delivery_address),'') IS NOT NULL))) OR " +
"(ip.creation_mode!='WAREHOUSE_TRANSFER' AND ((l.destination_type='FBA' AND NULLIF(TRIM(l.fba_warehouse_code),'') IS NOT NULL) OR " +
"(l.destination_type='PRIVATE' AND NULLIF(TRIM(l.delivery_address),'') IS NOT NULL AND NULLIF(TRIM(l.postal_code),'') IS NOT NULL " +
"AND NULLIF(TRIM(l.contact_name),'') IS NOT NULL AND NULLIF(TRIM(l.contact_phone),'') IS NOT NULL)) " +
"AND NULLIF(TRIM(l.contact_name),'') IS NOT NULL AND NULLIF(TRIM(l.contact_phone),'') IS NOT NULL)))) " +
"AND NOT EXISTS (SELECT 1 FROM ll_inbound_plan_pallet_allocation ha " +
"JOIN ll_inbound_plan_line hl ON hl.id=ha.line_id " +
"JOIN ll_order ho ON ho.id=hl.order_id AND ho.customer_id=ip.customer_id " +
"WHERE ha.pallet_id=p.id AND ho.detained=1) " +
"AND (p.warehouse_pallet_id IS NULL OR EXISTS (SELECT 1 FROM ll_inventory_position pos " +
"WHERE pos.customer_id=#{customerId} AND pos.pallet_id=p.warehouse_pallet_id AND pos.available_packages &gt; 0)) " +
"AND NOT EXISTS (SELECT 1 FROM ll_warehouse_transfer_inventory_allocation ta " +
"AND (ip.creation_mode='WAREHOUSE_TRANSFER' OR NOT EXISTS (SELECT 1 FROM ll_warehouse_transfer_inventory_allocation ta " +
"JOIN ll_inventory_position tp ON tp.id=ta.inventory_position_id " +
"WHERE ta.customer_id=#{customerId} AND ta.status='RESERVED' AND tp.pallet_id=p.warehouse_pallet_id) " +
"AND NOT EXISTS (SELECT 1 FROM ll_relabel_item rli JOIN ll_relabel_application rla ON rla.id=rli.application_id AND rla.customer_id=rli.customer_id " +
"WHERE ta.customer_id=#{customerId} AND ta.status='RESERVED' AND tp.pallet_id=p.warehouse_pallet_id)) " +
"AND (ip.creation_mode='WAREHOUSE_TRANSFER' OR NOT EXISTS (SELECT 1 FROM ll_relabel_item rli JOIN ll_relabel_application rla ON rla.id=rli.application_id AND rla.customer_id=rli.customer_id " +
"JOIN ll_inbound_plan_line rpl ON rpl.order_id=rli.source_inbound_order_id " +
"WHERE rli.customer_id=#{customerId} AND rli.source_inbound_order_id IS NOT NULL AND rpl.id IN " +
"(SELECT pa.line_id FROM ll_inbound_plan_pallet_allocation pa WHERE pa.pallet_id=p.id) " +
"AND rla.status NOT IN ('REJECTED','CANCELLED','COMPLETED')) " +
"AND rla.status NOT IN ('REJECTED','CANCELLED','COMPLETED'))) " +
"GROUP BY p.plan_id,p.id,p.warehouse_pallet_id,p.pallet_no,p.destination_warehouse_id," +
"p.destination_warehouse_code,p.destination_warehouse_name,p.allocated_volume_cbm " +
"ORDER BY p.id FOR UPDATE" +
@@ -241,7 +241,12 @@ public class DispatchPdaWorkflowService {
}
}
for (DispatchItem item : dispatchItems) {
if (item.getOrderId() != null) warehouseTransfers.markOutboundByOrder(customerId, item.getOrderId(), now);
// A relabel dispatch may consume only part of a warehouse-transfer
// pallet. Do not close the whole transfer by its parent order; the
// remaining cartons still need destination binding/outbound.
if (item.getOrderId() != null && !"RELABEL_ORDER".equals(item.getSourceBusinessType())) {
warehouseTransfers.markOutboundByOrder(customerId, item.getOrderId(), now);
}
if ("RELABEL_ORDER".equals(item.getSourceBusinessType()) && item.getSourceBusinessNo() != null) {
warehouseTransfers.markOutboundByRelabel(customerId, item.getSourceBusinessNo(), now);
}