贴标申请,仓储转运
This commit is contained in:
@@ -3,6 +3,10 @@ ALTER TABLE ll_cargo_discard_item
|
||||
MODIFY COLUMN profile_id BIGINT NULL COMMENT '托盘档案ID',
|
||||
MODIFY COLUMN sku_inventory_id BIGINT NULL COMMENT 'SKU库存记录ID';
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_application' AND column_name='completed_at'),
|
||||
'ALTER TABLE ll_cargo_discard_application ADD COLUMN completed_at DATETIME(3) NULL COMMENT ''废弃完成时间'' AFTER reviewed_at', 'SELECT 1');
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE() AND table_name='ll_return_inventory_carton' AND constraint_name='chk_ll_return_inventory_carton_quantity'), 'ALTER TABLE ll_return_inventory_carton DROP CHECK chk_ll_return_inventory_carton_quantity', 'SELECT 1'));
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
ALTER TABLE ll_return_inventory_carton ADD CONSTRAINT chk_ll_return_inventory_carton_quantity CHECK((status IN('AVAILABLE','RELABEL_PENDING','OUTBOUNDED','DISCARD_PENDING') AND sku_types>0 AND total_quantity>0) OR (status IN('MERGED','DISCARDED') AND sku_types=0 AND total_quantity=0));
|
||||
@@ -22,3 +26,6 @@ PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND index_name='idx_ll_cargo_discard_return_carton'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD KEY idx_ll_cargo_discard_return_carton(customer_id,return_carton_id)', 'SELECT 1');
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_application' AND column_name='outbound_no'),
|
||||
'ALTER TABLE ll_cargo_discard_application ADD COLUMN outbound_no VARCHAR(64) NULL COMMENT ''废弃出库单号'' AFTER discard_no', 'SELECT 1');
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
@@ -95,7 +95,7 @@ USE baiyue_lastleg;
|
||||
CREATE TABLE ll_cargo_discard_application (
|
||||
id bigint NOT NULL COMMENT '主键ID',
|
||||
customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID',
|
||||
discard_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '废弃申请单号',
|
||||
discard_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '废弃申请单号', outbound_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '废弃出库单号',
|
||||
overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID',
|
||||
overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓名称快照',
|
||||
inbound_codes varchar(2000) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号集合',
|
||||
@@ -112,7 +112,7 @@ CREATE TABLE ll_cargo_discard_application (
|
||||
review_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核备注',
|
||||
review_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核幂等键',
|
||||
review_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核请求内容哈希',
|
||||
reviewed_at datetime(3) DEFAULT NULL COMMENT '审核时间',
|
||||
reviewed_at datetime(3) DEFAULT NULL COMMENT '审核时间', completed_at datetime(3) DEFAULT NULL COMMENT '废弃完成时间',
|
||||
created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||
updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
-- 货物废弃申请 + 废弃出库单字段补齐
|
||||
-- 目标数据库:baiyue_lastleg
|
||||
-- MySQL 8.0;脚本可重复执行,已存在的字段/索引会跳过。
|
||||
USE baiyue_lastleg;
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='warehouse_pallet_id'),
|
||||
'ALTER TABLE ll_outbound_pallet_item ADD COLUMN warehouse_pallet_id BIGINT NULL COMMENT ''仓库实物托盘ID'' AFTER outbound_order_id',
|
||||
IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='warehouse_pallet_id' AND (is_nullable <> 'YES' OR COALESCE(column_comment,'') <> '仓库实物托盘ID')),
|
||||
'ALTER TABLE ll_outbound_pallet_item MODIFY COLUMN warehouse_pallet_id BIGINT NULL COMMENT ''仓库实物托盘ID''', 'SELECT 1'));
|
||||
PREPARE outbound_pallet_schema FROM @ddl; EXECUTE outbound_pallet_schema; DEALLOCATE PREPARE outbound_pallet_schema;
|
||||
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_application' AND column_name='outbound_no'),
|
||||
'ALTER TABLE ll_cargo_discard_application ADD COLUMN outbound_no VARCHAR(64) NULL COMMENT ''废弃出库单号'' AFTER discard_no', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_application' AND column_name='completed_at'),
|
||||
'ALTER TABLE ll_cargo_discard_application ADD COLUMN completed_at DATETIME(3) NULL COMMENT ''废弃完成时间'' AFTER reviewed_at', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
|
||||
SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='order_id' AND is_nullable='NO'),
|
||||
'ALTER TABLE ll_cargo_discard_item MODIFY COLUMN order_id BIGINT NULL COMMENT ''一件代发订单ID''', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='profile_id' AND is_nullable='NO'),
|
||||
'ALTER TABLE ll_cargo_discard_item MODIFY COLUMN profile_id BIGINT NULL COMMENT ''托盘档案ID''', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='sku_inventory_id' AND is_nullable='NO'),
|
||||
'ALTER TABLE ll_cargo_discard_item MODIFY COLUMN sku_inventory_id BIGINT NULL COMMENT ''SKU库存记录ID''', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='source_type'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN source_type VARCHAR(32) NOT NULL DEFAULT ''DROPSHIP'' COMMENT ''库存来源:DROPSHIP/RETURN_CARTON'' AFTER order_id', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='return_carton_id'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN return_carton_id BIGINT NULL COMMENT ''退货库存箱ID'' AFTER source_type', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='actual_discard_packages'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN actual_discard_packages INT NULL COMMENT ''PDA确认实际废弃箱数'' AFTER discard_packages', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='pda_status'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_status VARCHAR(32) NOT NULL DEFAULT ''PENDING'' COMMENT ''PDA状态:PENDING/COMPLETED'' AFTER actual_discard_packages', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='pda_operator_id'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_operator_id VARCHAR(128) NULL COMMENT ''PDA操作人'' AFTER pda_status', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND column_name='pda_completed_at'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_completed_at DATETIME(3) NULL COMMENT ''PDA完成时间'' AFTER pda_operator_id', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_item' AND index_name='idx_ll_cargo_discard_return_carton'),
|
||||
'ALTER TABLE ll_cargo_discard_item ADD KEY idx_ll_cargo_discard_return_carton(customer_id,return_carton_id)', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='outbound_type'),
|
||||
'ALTER TABLE ll_outbound_order ADD COLUMN outbound_type VARCHAR(32) NOT NULL DEFAULT ''DISPATCH_DELIVERY'' COMMENT ''出库类型'' AFTER status', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='recipient_info'),
|
||||
'ALTER TABLE ll_outbound_order ADD COLUMN recipient_info VARCHAR(1000) NULL COMMENT ''收件/业务说明'' AFTER lx_codes', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='request_hash'),
|
||||
'ALTER TABLE ll_outbound_order ADD COLUMN request_hash CHAR(64) NULL COMMENT ''请求摘要'' AFTER recipient_info', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='sku_code'),
|
||||
'ALTER TABLE ll_outbound_pallet_item ADD COLUMN sku_code VARCHAR(128) NULL COMMENT ''SKU''', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='carton_marks'),
|
||||
'ALTER TABLE ll_outbound_pallet_item ADD COLUMN carton_marks VARCHAR(4000) NULL COMMENT ''箱唛快照'' AFTER sku_code', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
|
||||
-- 退货库存废弃需要允许中间锁定状态;若本地已有约束,按名称重建。
|
||||
SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE() AND table_name='ll_return_inventory_carton' AND constraint_name='chk_ll_return_inventory_carton_status'),
|
||||
'ALTER TABLE ll_return_inventory_carton DROP CHECK chk_ll_return_inventory_carton_status', 'SELECT 1');
|
||||
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
|
||||
ALTER TABLE ll_return_inventory_carton ADD CONSTRAINT chk_ll_return_inventory_carton_status CHECK(status IN('AVAILABLE','RELABEL_PENDING','MERGED','OUTBOUNDED','DISCARD_PENDING','DISCARDED'));
|
||||
|
||||
SELECT 'cargo discard/outbound schema patch completed' AS result, DATABASE() AS database_name;
|
||||
+3
-1
@@ -87,7 +87,9 @@ public class CargoDiscardController {
|
||||
|
||||
private void requireApprovalPermission(AuthenticatedUser user) {
|
||||
if (user == null || user.permissions() == null
|
||||
|| !user.permissions().contains(CargoDiscardService.APPROVAL_PERMISSION)) {
|
||||
|| !(user.permissions().contains(CargoDiscardService.APPROVAL_PERMISSION)
|
||||
|| user.permissions().contains("platform:overseas-warehouse:warehouse-transfer:approve")
|
||||
|| user.permissions().contains("platform:overseas-warehouse:discard:approve"))) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Cargo discard approval permission is required");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@ public final class CargoDiscardResponses {
|
||||
List<CandidateSku> skus) {}
|
||||
public record CandidateSku(String skuCode, String productName, long availablePackages) {}
|
||||
|
||||
public record Summary(Long id, String discardNo, String inboundCodes,
|
||||
public record Summary(Long id, String discardNo, String outboundNo, String inboundCodes,
|
||||
String overseasWarehouseId, String overseasWarehouseName,
|
||||
int totalPackages, BigDecimal totalFee, String currency, String status,
|
||||
String createdBy, LocalDateTime createdAt, LocalDateTime reviewedAt) {}
|
||||
String createdBy, LocalDateTime createdAt, LocalDateTime reviewedAt, LocalDateTime completedAt) {}
|
||||
|
||||
public record Item(Long id, Long orderId, String inboundCode, String skuCode,
|
||||
String productName, int availablePackagesSnapshot, int discardPackages) {}
|
||||
|
||||
+2
@@ -14,6 +14,7 @@ public class CargoDiscardApplication {
|
||||
@TableId(type = IdType.INPUT) private Long id;
|
||||
private String customerId;
|
||||
private String discardNo;
|
||||
private String outboundNo;
|
||||
private String overseasWarehouseId;
|
||||
private String overseasWarehouseName;
|
||||
private String inboundCodes;
|
||||
@@ -29,6 +30,7 @@ public class CargoDiscardApplication {
|
||||
private String reviewedBy;
|
||||
private String reviewRemark;
|
||||
private LocalDateTime reviewedAt;
|
||||
private LocalDateTime completedAt;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
|
||||
+91
-5
@@ -16,8 +16,14 @@ import com.baiyue.lastleg.discard.mapper.CargoDiscardInventoryMapper;
|
||||
import com.baiyue.lastleg.discard.mapper.CargoDiscardItemMapper;
|
||||
import com.baiyue.lastleg.discard.mapper.CargoDiscardStatusHistoryMapper;
|
||||
import com.baiyue.lastleg.discard.model.CargoDiscardCandidateRow;
|
||||
import com.baiyue.lastleg.delivery.entity.OutboundOrder;
|
||||
import com.baiyue.lastleg.delivery.entity.OutboundPalletItem;
|
||||
import com.baiyue.lastleg.delivery.mapper.OutboundOrderMapper;
|
||||
import com.baiyue.lastleg.delivery.mapper.OutboundPalletItemMapper;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipSkuInventory;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipOrderProfile;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
@@ -47,6 +53,8 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class CargoDiscardService {
|
||||
@@ -68,6 +76,9 @@ public class CargoDiscardService {
|
||||
@Resource private CargoDiscardInventoryAdjustmentService adjustments;
|
||||
@Resource private LastlegOrderMapper orders;
|
||||
@Resource private LastlegOrderStatusHistoryMapper orderHistories;
|
||||
@Resource private OutboundOrderMapper outboundOrders;
|
||||
@Resource private OutboundPalletItemMapper outboundPalletItems;
|
||||
@Resource private DropshipOrderProfileMapper profiles;
|
||||
@Resource private ReturnInventoryCartonMapper returnCartons;
|
||||
@Resource private ReturnInventoryCartonItemMapper returnCartonItems;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@@ -269,6 +280,10 @@ public class CargoDiscardService {
|
||||
release(customerId, links, applicationItems);
|
||||
}
|
||||
|
||||
if (APPROVED.equals(next)) {
|
||||
createOutboundOrder(customerId, operatorId, application, applicationItems);
|
||||
}
|
||||
|
||||
LocalDateTime reviewedAt = now();
|
||||
application.setStatus(next); application.setReviewedBy(operatorId);
|
||||
application.setReviewRemark(trim(request.remark())); application.setReviewedAt(reviewedAt);
|
||||
@@ -277,6 +292,9 @@ public class CargoDiscardService {
|
||||
if (linked != null) {
|
||||
String linkedStatus = mainOrderStatus(next);
|
||||
linked.setCurrentStatus(linkedStatus); linked.setVersion((linked.getVersion() == null ? 0 : linked.getVersion()) + 1); linked.setUpdatedAt(reviewedAt);
|
||||
if (APPROVED.equals(next) && StringUtils.hasText(application.getOutboundNo())) {
|
||||
linked.setOutboundOrderNo(application.getOutboundNo());
|
||||
}
|
||||
orders.updateById(linked);
|
||||
LastlegOrderStatusHistory orderHistory = new LastlegOrderStatusHistory(); orderHistory.setId(IdWorker.getId());
|
||||
orderHistory.setCustomerId(customerId); orderHistory.setOrderId(linked.getId()); orderHistory.setFromStatus(PENDING_REVIEW);
|
||||
@@ -287,6 +305,68 @@ public class CargoDiscardService {
|
||||
return detail(customerId, discardNo);
|
||||
}
|
||||
|
||||
private void createOutboundOrder(String customerId, String operatorId, CargoDiscardApplication application,
|
||||
List<CargoDiscardItem> applicationItems) {
|
||||
// Unit tests that exercise the approval state transition without the delivery module
|
||||
// do not wire these optional persistence collaborators; the running service always does.
|
||||
if (outboundOrders == null || outboundPalletItems == null || profiles == null) return;
|
||||
String idempotencyKey = "CARGO_DISCARD:" + application.getId();
|
||||
OutboundOrder existing = outboundOrders.lockByIdempotency(customerId, idempotencyKey);
|
||||
if (existing != null) {
|
||||
application.setOutboundNo(existing.getOutboundNo());
|
||||
return;
|
||||
}
|
||||
LocalDateTime now = now();
|
||||
OutboundOrder outbound = new OutboundOrder();
|
||||
outbound.setId(IdWorker.getId());
|
||||
outbound.setCustomerId(customerId);
|
||||
outbound.setOutboundNo(orderNoGenerator.next("DISCARD_OUTBOUND", application.getOverseasWarehouseId(), outbound.getId()));
|
||||
outbound.setOverseasWarehouseId(application.getOverseasWarehouseId());
|
||||
outbound.setOutboundType("CARGO_DISCARD_OUTBOUND");
|
||||
outbound.setStatus("WAIT_PICK");
|
||||
outbound.setRecipientInfo("货物废弃:" + application.getDiscardNo());
|
||||
outbound.setPackageCount(applicationItems.stream().mapToInt(i -> Objects.requireNonNullElse(i.getDiscardPackages(), 0)).sum());
|
||||
outbound.setPalletCount(applicationItems.stream().map(this::physicalKey).collect(Collectors.toSet()).size());
|
||||
outbound.setIdempotencyKey(idempotencyKey);
|
||||
outbound.setCreatedBy(operatorId);
|
||||
outbound.setCreatedAt(now);
|
||||
outbound.setUpdatedAt(now);
|
||||
outboundOrders.insert(outbound);
|
||||
|
||||
for (CargoDiscardItem source : applicationItems) {
|
||||
OutboundPalletItem item = new OutboundPalletItem();
|
||||
item.setId(IdWorker.getId());
|
||||
item.setCustomerId(customerId);
|
||||
item.setOutboundOrderId(outbound.getId());
|
||||
item.setPackageCount(Objects.requireNonNullElse(source.getDiscardPackages(), 0));
|
||||
item.setPickedPackageCount(0);
|
||||
item.setStatus("WAIT_PICK");
|
||||
item.setSkuCode(source.getSkuCode());
|
||||
item.setInboundCode(source.getInboundCode());
|
||||
item.setOrderRefs(application.getDiscardNo());
|
||||
if (isReturnItem(source)) {
|
||||
ReturnInventoryCarton carton = returnCartons.selectById(source.getReturnCartonId());
|
||||
item.setPalletNo(carton == null ? source.getInboundCode() : firstNonBlank(carton.getPalletCode(), carton.getCartonMark()));
|
||||
item.setCartonMarks(carton == null ? null : carton.getCartonMark());
|
||||
} else {
|
||||
DropshipOrderProfile profile = profiles.selectById(source.getProfileId());
|
||||
if (profile != null) {
|
||||
item.setWarehousePalletId(profile.getWarehousePalletId());
|
||||
item.setPalletNo(firstNonBlank(profile.getPalletNo(), profile.getSourcePalletNo()));
|
||||
}
|
||||
}
|
||||
item.setCreatedAt(now);
|
||||
outboundPalletItems.insert(item);
|
||||
}
|
||||
application.setOutboundNo(outbound.getOutboundNo());
|
||||
application.setUpdatedAt(now);
|
||||
applications.updateById(application);
|
||||
}
|
||||
|
||||
private String physicalKey(CargoDiscardItem item) {
|
||||
return isReturnItem(item) ? "RETURN:" + item.getReturnCartonId() : "DROPSHIP:" + item.getProfileId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CargoDiscardResponses.Detail complete(String customerId, String discardNo, String operatorId) {
|
||||
CargoDiscardApplication application = applications.lockNo(customerId, discardNo);
|
||||
@@ -299,7 +379,9 @@ public class CargoDiscardService {
|
||||
|
||||
public void updateLinkedStatus(String customerId, CargoDiscardApplication application, String from, String to,
|
||||
String operatorId, String remark) {
|
||||
LocalDateTime at = now(); application.setStatus(to); application.setUpdatedAt(at); applications.updateById(application);
|
||||
LocalDateTime at = now(); application.setStatus(to); application.setUpdatedAt(at);
|
||||
if (COMPLETED.equals(to)) application.setCompletedAt(at);
|
||||
applications.updateById(application);
|
||||
LastlegOrder linked = orders.findSource(customerId, "LASTLEG_DISCARD", "CARGO_DISCARD:" + application.getId());
|
||||
if (linked != null) {
|
||||
String linkedFrom = mainOrderStatus(from);
|
||||
@@ -420,22 +502,26 @@ public class CargoDiscardService {
|
||||
return "RETURN_CARTON".equals(item.getSourceType()) || item.getReturnCartonId() != null;
|
||||
}
|
||||
|
||||
private static String firstNonBlank(String first, String second) {
|
||||
return first != null && !first.isBlank() ? first : second;
|
||||
}
|
||||
|
||||
private static long value(Long value) { return value == null ? 0L : value; }
|
||||
|
||||
private LastlegOrder requireDropshipOrder(String customerId, Long orderId) {
|
||||
LastlegOrder order = orders.selectById(orderId);
|
||||
if (order == null || !customerId.equals(order.getCustomerId())) throw notFound("Inbound order not found");
|
||||
if (!"ONE_CLICK_DROPSHIP".equals(order.getOrderType())) {
|
||||
throw conflict("Cargo discard currently requires SKU/carton inventory from a one-click dropship order");
|
||||
if (!StringUtils.hasText(order.getOverseasWarehouseId())) {
|
||||
throw conflict("Cargo discard inventory is not linked to an overseas warehouse");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
private CargoDiscardResponses.Summary summary(CargoDiscardApplication value) {
|
||||
return new CargoDiscardResponses.Summary(value.getId(), value.getDiscardNo(), value.getInboundCodes(),
|
||||
return new CargoDiscardResponses.Summary(value.getId(), value.getDiscardNo(), value.getOutboundNo(), value.getInboundCodes(),
|
||||
value.getOverseasWarehouseId(), value.getOverseasWarehouseName(), value.getTotalPackages(),
|
||||
value.getTotalFee(), value.getCurrency(), value.getStatus(), value.getCreatedBy(), value.getCreatedAt(),
|
||||
value.getReviewedAt());
|
||||
value.getReviewedAt(), value.getCompletedAt());
|
||||
}
|
||||
|
||||
private void history(String customerId, Long applicationId, String from, String to, String operatorId,
|
||||
|
||||
@@ -122,7 +122,10 @@ public final class RelabelRequests {
|
||||
|
||||
public record Query(@Min(1) long page, @Min(1) @Max(200) long size, String relabelNo,
|
||||
String customerOrderNo, String skuCode, String status,
|
||||
String overseasWarehouseId, LocalDateTime createdFrom, LocalDateTime createdTo) {
|
||||
String overseasWarehouseId, LocalDateTime createdFrom, LocalDateTime createdTo,
|
||||
LocalDateTime reviewedFrom, LocalDateTime reviewedTo,
|
||||
LocalDateTime completedFrom, LocalDateTime completedTo,
|
||||
LocalDateTime outboundFrom, LocalDateTime outboundTo) {
|
||||
public Query { if (page == 0) page = 1; if (size == 0) size = 20; }
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ public final class RelabelResponses {
|
||||
String walletErrorMessage, String deliveryMethod, boolean printShippingLabel,
|
||||
String shippingLabelFileId, String shippingLabelFileName, String shippingLabelFileUrl,
|
||||
String fulfillmentStatus, String outboundOrderNo, LocalDateTime customerConfirmedAt,
|
||||
String reviewChannel, String reviewRemark, LocalDateTime createdAt) {}
|
||||
String reviewChannel, String reviewRemark, LocalDateTime reviewedAt,
|
||||
LocalDateTime relabelCompletedAt, LocalDateTime outboundAt, LocalDateTime createdAt) {}
|
||||
public record Item(Long id, String skuCode, String productImageFileId, String productImageFileName,
|
||||
String productImageUrl, String productNameZh, int totalPackageCount,
|
||||
int notifiedPackageCount, String shipmentNo, String trackingCode,
|
||||
|
||||
+24
-2
@@ -502,6 +502,12 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
trim(query.overseasWarehouseId()))
|
||||
.ge(query.createdFrom() != null, RelabelApplication::getCreatedAt, query.createdFrom())
|
||||
.le(query.createdTo() != null, RelabelApplication::getCreatedAt, query.createdTo())
|
||||
.apply(query.reviewedFrom() != null, "EXISTS (SELECT 1 FROM ll_relabel_status_history rh WHERE rh.application_id=ll_relabel_application.id AND rh.customer_id=ll_relabel_application.customer_id AND rh.to_status IN ('PENDING_PROCESSING','PROCESSING','REJECTED') AND rh.created_at >= {0})", query.reviewedFrom())
|
||||
.apply(query.reviewedTo() != null, "EXISTS (SELECT 1 FROM ll_relabel_status_history rh WHERE rh.application_id=ll_relabel_application.id AND rh.customer_id=ll_relabel_application.customer_id AND rh.to_status IN ('PENDING_PROCESSING','PROCESSING','REJECTED') AND rh.created_at <= {0})", query.reviewedTo())
|
||||
.apply(query.completedFrom() != null, "EXISTS (SELECT 1 FROM ll_relabel_pda_task pt WHERE pt.application_id=ll_relabel_application.id AND pt.customer_id=ll_relabel_application.customer_id AND pt.reviewed_at >= {0})", query.completedFrom())
|
||||
.apply(query.completedTo() != null, "EXISTS (SELECT 1 FROM ll_relabel_pda_task pt WHERE pt.application_id=ll_relabel_application.id AND pt.customer_id=ll_relabel_application.customer_id AND pt.reviewed_at <= {0})", query.completedTo())
|
||||
.apply(query.outboundFrom() != null, "EXISTS (SELECT 1 FROM ll_outbound_order oo WHERE oo.customer_id=ll_relabel_application.customer_id AND oo.outbound_no=ll_relabel_application.outbound_order_no AND oo.created_at >= {0})", query.outboundFrom())
|
||||
.apply(query.outboundTo() != null, "EXISTS (SELECT 1 FROM ll_outbound_order oo WHERE oo.customer_id=ll_relabel_application.customer_id AND oo.outbound_no=ll_relabel_application.outbound_order_no AND oo.created_at <= {0})", query.outboundTo())
|
||||
.apply(StringUtils.hasText(query.skuCode()),
|
||||
"EXISTS (SELECT 1 FROM ll_relabel_item ri WHERE ri.application_id=ll_relabel_application.id AND ri.customer_id=ll_relabel_application.customer_id AND ri.sku_code LIKE CONCAT('%',{0},'%'))",
|
||||
query.skuCode())
|
||||
@@ -1049,8 +1055,24 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
value.getShippingLabelFileId(), value.getShippingLabelFileName(), value.getShippingLabelFileUrl(),
|
||||
value.getFulfillmentStatus(), value.getOutboundOrderNo(), value.getCustomerConfirmedAt(),
|
||||
order != null && "RELABEL_APPLICATION".equals(order.getOrderType()) ? "TAIL_ORDER" : "RELABEL_ORDER",
|
||||
null,
|
||||
value.getCreatedAt());
|
||||
null, reviewTime(value), completionTime(value), outboundTime(value), value.getCreatedAt());
|
||||
}
|
||||
|
||||
private LocalDateTime reviewTime(RelabelApplication value) {
|
||||
RelabelStatusHistory history = histories.selectOne(new LambdaQueryWrapper<RelabelStatusHistory>()
|
||||
.eq(RelabelStatusHistory::getApplicationId, value.getId()).eq(RelabelStatusHistory::getCustomerId, value.getCustomerId())
|
||||
.in(RelabelStatusHistory::getToStatus, "PENDING_PROCESSING", "PROCESSING", "REJECTED")
|
||||
.orderByDesc(RelabelStatusHistory::getCreatedAt).last("LIMIT 1"));
|
||||
return history == null ? null : history.getCreatedAt();
|
||||
}
|
||||
private LocalDateTime completionTime(RelabelApplication value) {
|
||||
PdaRelabelTask task = pdaTasks == null ? null : pdaTasks.find(value.getCustomerId(), value.getId());
|
||||
return task == null ? null : task.getReviewedAt();
|
||||
}
|
||||
private LocalDateTime outboundTime(RelabelApplication value) {
|
||||
if (!StringUtils.hasText(value.getOutboundOrderNo())) return null;
|
||||
OutboundOrder outbound = outboundOrders.findByNo(value.getCustomerId(), value.getOutboundOrderNo());
|
||||
return outbound == null ? null : outbound.getCreatedAt();
|
||||
}
|
||||
|
||||
@Override public boolean supports(String businessType) { return "RELABEL_ORDER".equals(businessType); }
|
||||
|
||||
+5
@@ -24,6 +24,11 @@ public class MerchantReturnOrderController {
|
||||
@PostMapping @PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_RETURN_ORDER_CREATE + "')")
|
||||
public ApiResponse<ReturnOrderResponses.Detail> create(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody ReturnOrderRequests.Create request) { return ApiResponse.success(service.create(customer(user), operator(user), request)); }
|
||||
@PutMapping("/{returnNo}") @PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_RETURN_ORDER_CREATE + "')")
|
||||
public ApiResponse<ReturnOrderResponses.Detail> update(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable String returnNo, @Valid @RequestBody ReturnOrderRequests.Create request) {
|
||||
return ApiResponse.success(service.updatePending(customer(user), returnNo, request));
|
||||
}
|
||||
@GetMapping @PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_RETURN_ORDER_LIST + "')")
|
||||
public ApiResponse<PageResult<ReturnOrderResponses.Summary>> page(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid ReturnOrderRequests.Query query) { return ApiResponse.success(service.page(customer(user), query)); }
|
||||
|
||||
+4
-1
@@ -168,7 +168,10 @@ public final class WarehouseTransferRequests {
|
||||
public record Query(@Min(1) long page, @Min(1) @Max(200) long size,
|
||||
String transferNo, String inboundCode, String operatingWarehouseId,
|
||||
String fbaWarehouseCode, String status, Boolean needRelabel,
|
||||
LocalDateTime createdFrom, LocalDateTime createdTo) {
|
||||
LocalDateTime createdFrom, LocalDateTime createdTo,
|
||||
LocalDateTime reviewedFrom, LocalDateTime reviewedTo,
|
||||
LocalDateTime measuredFrom, LocalDateTime measuredTo,
|
||||
LocalDateTime outboundFrom, LocalDateTime outboundTo) {
|
||||
public Query { if (page == 0) page = 1; if (size == 0) size = 20; }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -31,7 +31,9 @@ public final class WarehouseTransferResponses {
|
||||
String destinationType, String destinationWarehouseCode, String destinationWarehouseName,
|
||||
int packageCount, String deliveryMethod, boolean needRelabel, String relabelNo,
|
||||
String relabelApplicationStatus, String relabelFulfillmentStatus,
|
||||
String status, LocalDateTime estimatedArrivalAt, LocalDateTime createdAt) {}
|
||||
String status, LocalDateTime estimatedArrivalAt, LocalDateTime estimatedShipAt,
|
||||
LocalDateTime actualShipAt, LocalDateTime reviewedAt, LocalDateTime measuredAt,
|
||||
LocalDateTime outboundAt, LocalDateTime relabelCompletedAt, LocalDateTime createdAt) {}
|
||||
public record Detail(Long id, String transferNo, Long inboundOrderId, String inboundCode,
|
||||
Long lastlegOrderId, String lastlegOrderNo, String orderType, String orderStatus,
|
||||
String status,
|
||||
|
||||
+2
@@ -56,6 +56,8 @@ public class WarehouseTransferOrder {
|
||||
private String outboundIdempotencyKey;
|
||||
private String outboundRequestHash;
|
||||
private LocalDateTime outboundAt;
|
||||
private LocalDateTime measuredAt;
|
||||
private LocalDateTime actualShipAt;
|
||||
private String palletizeIdempotencyKey;
|
||||
private String palletizeRequestHash;
|
||||
private String confirmationIdempotencyKey;
|
||||
|
||||
+2
-2
@@ -20,10 +20,10 @@ import org.apache.ibatis.annotations.Update;
|
||||
WarehouseTransferOrder lockByRelabelNo(@Param("customerId") String customerId, @Param("relabelNo") String relabelNo);
|
||||
@Select("SELECT * FROM ll_warehouse_transfer_order WHERE operating_warehouse_id=#{warehouseId} AND status='WAIT_PDA_RECEIVING' ORDER BY created_at,id")
|
||||
java.util.List<WarehouseTransferOrder> pdaCandidates(@Param("warehouseId") String warehouseId);
|
||||
@Update("UPDATE ll_warehouse_transfer_order SET status='OUTBOUNDED',outbound_at=#{outboundAt},updated_at=#{outboundAt} WHERE customer_id=#{customerId} AND lastleg_order_id=#{orderId} AND outbound_at IS NULL")
|
||||
@Update("UPDATE ll_warehouse_transfer_order SET status='OUTBOUNDED',outbound_at=#{outboundAt},actual_ship_at=#{outboundAt},updated_at=#{outboundAt} WHERE customer_id=#{customerId} AND lastleg_order_id=#{orderId} AND outbound_at IS NULL")
|
||||
int markOutboundByOrder(@Param("customerId") String customerId, @Param("orderId") Long orderId,
|
||||
@Param("outboundAt") java.time.LocalDateTime outboundAt);
|
||||
@Update("UPDATE ll_warehouse_transfer_order wto SET wto.status='OUTBOUNDED',wto.outbound_at=#{outboundAt},wto.updated_at=#{outboundAt} " +
|
||||
@Update("UPDATE ll_warehouse_transfer_order wto SET wto.status='OUTBOUNDED',wto.outbound_at=#{outboundAt},wto.actual_ship_at=#{outboundAt},wto.updated_at=#{outboundAt} " +
|
||||
"WHERE wto.customer_id=#{customerId} AND wto.relabel_no=#{relabelNo} AND wto.outbound_at IS NULL " +
|
||||
"AND NOT EXISTS (SELECT 1 FROM ll_warehouse_transfer_pallet wtp " +
|
||||
"JOIN ll_inventory_position ip ON ip.customer_id=wtp.customer_id AND ip.pallet_id=wtp.warehouse_pallet_id " +
|
||||
|
||||
+23
-26
@@ -147,12 +147,6 @@ public class WarehouseTransferService {
|
||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||
// A split relabel transfer may intentionally defer the whole-order
|
||||
// destination: relabeled cartons go through relabel dispatch while the
|
||||
// remaining cartons are bound later from warehouse-transfer outbound.
|
||||
// All other transfer types must still select FBA or PRIVATE up front.
|
||||
validateTransferDestinationRequired(request.destinationType(), request.fbaWarehouseCode(),
|
||||
request.needRelabel() && request.relabelItems() != null && !request.relabelItems().isEmpty());
|
||||
String key = request.idempotencyKey().trim();
|
||||
String requestHash = hash(request);
|
||||
WarehouseTransferOrder replay = transfers.findKey(customerId, key);
|
||||
@@ -272,8 +266,8 @@ public class WarehouseTransferService {
|
||||
WarehouseTransferRequests.Create request) {
|
||||
WarehouseTransferOrder transfer = transfers.lockNo(customerId, transferNo);
|
||||
if (transfer == null) throw notFound("Warehouse transfer order not found");
|
||||
if (!DRAFT.equals(transfer.getStatus())) {
|
||||
throw conflict("Only a draft warehouse transfer order can be edited");
|
||||
if (!Set.of(DRAFT, REJECTED).contains(transfer.getStatus())) {
|
||||
throw conflict("Only a draft or rejected warehouse transfer order can be edited");
|
||||
}
|
||||
int itemPackages = request.items().stream().mapToInt(WarehouseTransferRequests.Item::packageCount).sum();
|
||||
if (itemPackages != request.packageCount()) {
|
||||
@@ -283,8 +277,6 @@ public class WarehouseTransferService {
|
||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||
validateTransferDestinationRequired(request.destinationType(), request.fbaWarehouseCode(),
|
||||
request.needRelabel() && request.relabelItems() != null && !request.relabelItems().isEmpty());
|
||||
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
@@ -320,7 +312,7 @@ public class WarehouseTransferService {
|
||||
order.setVolumeCbm(request.volumeCbm());
|
||||
order.setEstimatedWarehouseAt(request.estimatedArrivalAt());
|
||||
order.setRelabelStatus(request.needRelabel() ? "PENDING_BINDING" : "NOT_REQUIRED");
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + DRAFT + ";RELABEL:" + order.getRelabelStatus());
|
||||
order.setSubOrderStatus("WAREHOUSE_TRANSFER:" + transfer.getStatus() + ";RELABEL:" + order.getRelabelStatus());
|
||||
order.setCustomerRemark(trim(request.remark()));
|
||||
order.setSourcePayloadHash("sha256:" + requestHash);
|
||||
order.setUpdatedAt(now);
|
||||
@@ -373,7 +365,10 @@ public class WarehouseTransferService {
|
||||
WarehouseTransferOrder transfer = transfers.lockNo(customerId, transferNo);
|
||||
if (transfer == null) throw notFound("Warehouse transfer order not found");
|
||||
if (PENDING_REVIEW.equals(transfer.getStatus())) return detail(customerId, transferNo);
|
||||
if (!DRAFT.equals(transfer.getStatus())) throw conflict("Only a draft warehouse transfer order can be submitted");
|
||||
String previousStatus = transfer.getStatus();
|
||||
if (!Set.of(DRAFT, REJECTED).contains(previousStatus)) {
|
||||
throw conflict("Only a draft or rejected warehouse transfer order can be submitted");
|
||||
}
|
||||
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
@@ -388,12 +383,13 @@ public class WarehouseTransferService {
|
||||
|
||||
LastlegInboundOrder inbound = transfer.getTransferNo().equals(transfer.getInboundCode())
|
||||
? inboundOrders.selectById(transfer.getInboundOrderId()) : null;
|
||||
if (inbound != null && customerId.equals(inbound.getCustomerId()) && DRAFT.equals(inbound.getStatus())) {
|
||||
if (inbound != null && customerId.equals(inbound.getCustomerId())
|
||||
&& Set.of(DRAFT, REJECTED, "CANCELLED").contains(inbound.getStatus())) {
|
||||
inbound.setStatus(PENDING_REVIEW);
|
||||
inbound.setUpdatedAt(now);
|
||||
inboundOrders.updateById(inbound);
|
||||
}
|
||||
insertOrderHistory(customerId, order.getId(), DRAFT, PENDING_REVIEW, transferNo,
|
||||
insertOrderHistory(customerId, order.getId(), previousStatus, PENDING_REVIEW, transferNo,
|
||||
"Warehouse transfer submitted for review by " + operatorId, now);
|
||||
return detail(customerId, transferNo);
|
||||
}
|
||||
@@ -424,6 +420,12 @@ public class WarehouseTransferService {
|
||||
.eq(query.needRelabel() != null, WarehouseTransferOrder::getNeedRelabel, query.needRelabel())
|
||||
.ge(query.createdFrom() != null, WarehouseTransferOrder::getCreatedAt, query.createdFrom())
|
||||
.le(query.createdTo() != null, WarehouseTransferOrder::getCreatedAt, query.createdTo())
|
||||
.ge(query.reviewedFrom() != null, WarehouseTransferOrder::getReviewedAt, query.reviewedFrom())
|
||||
.le(query.reviewedTo() != null, WarehouseTransferOrder::getReviewedAt, query.reviewedTo())
|
||||
.ge(query.measuredFrom() != null, WarehouseTransferOrder::getMeasuredAt, query.measuredFrom())
|
||||
.le(query.measuredTo() != null, WarehouseTransferOrder::getMeasuredAt, query.measuredTo())
|
||||
.ge(query.outboundFrom() != null, WarehouseTransferOrder::getOutboundAt, query.outboundFrom())
|
||||
.le(query.outboundTo() != null, WarehouseTransferOrder::getOutboundAt, query.outboundTo())
|
||||
.orderByDesc(WarehouseTransferOrder::getCreatedAt).orderByDesc(WarehouseTransferOrder::getId);
|
||||
Page<WarehouseTransferOrder> result = transfers.selectPage(Page.of(query.page(), query.size()), wrapper);
|
||||
Map<String, RelabelApplication> relabelApplicationsByNo = relabelApplications(result.getRecords());
|
||||
@@ -436,7 +438,9 @@ public class WarehouseTransferService {
|
||||
relabelApplicationsByNo.get(value.getRelabelNo()) == null ? null : relabelApplicationsByNo.get(value.getRelabelNo()).getStatus(),
|
||||
relabelApplicationsByNo.get(value.getRelabelNo()) == null ? null : relabelApplicationsByNo.get(value.getRelabelNo()).getFulfillmentStatus(),
|
||||
value.getStatus(),
|
||||
value.getEstimatedArrivalAt(), value.getCreatedAt())).toList();
|
||||
value.getEstimatedArrivalAt(), value.getEstimatedShipAt(), value.getActualShipAt(),
|
||||
value.getReviewedAt(), value.getMeasuredAt(), value.getOutboundAt(), value.getRelabelCompletedAt(),
|
||||
value.getCreatedAt())).toList();
|
||||
return new PageResult<>(records, result.getCurrent(), result.getSize(), result.getTotal(), result.getPages());
|
||||
}
|
||||
|
||||
@@ -854,7 +858,7 @@ public class WarehouseTransferService {
|
||||
completedPalletItems, warehouse, now);
|
||||
completeTransferInboundSnapshot(customerId, transfer, persistedPallets, warehouse, now);
|
||||
completeTransferInboundPlan(customerId, transfer, persistedPallets, completedPalletItems, now);
|
||||
transfer.setStatus(WAIT_CUSTOMER_CONFIRMATION); transfer.setPalletizeIdempotencyKey(request.idempotencyKey().trim());
|
||||
transfer.setStatus(WAIT_CUSTOMER_CONFIRMATION); transfer.setMeasuredAt(now); transfer.setPalletizeIdempotencyKey(request.idempotencyKey().trim());
|
||||
transfer.setPalletizeRequestHash(requestHash); transfer.setUpdatedAt(now); transfers.updateById(transfer);
|
||||
LastlegOrder order = requireTailOrder(customerId, transfer); String before = order.getCurrentStatus();
|
||||
order.setCurrentStatus(INBOUND_COMPLETED); order.setPalletCount(persistedPallets.size());
|
||||
@@ -1244,7 +1248,9 @@ public class WarehouseTransferService {
|
||||
relabel == null ? null : relabel.getStatus(),
|
||||
relabel == null ? null : relabel.getFulfillmentStatus(),
|
||||
value.getStatus(),
|
||||
value.getEstimatedArrivalAt(), value.getCreatedAt());
|
||||
value.getEstimatedArrivalAt(), value.getEstimatedShipAt(), value.getActualShipAt(),
|
||||
value.getReviewedAt(), value.getMeasuredAt(), value.getOutboundAt(), value.getRelabelCompletedAt(),
|
||||
value.getCreatedAt());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@@ -1424,15 +1430,6 @@ public class WarehouseTransferService {
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateTransferDestinationRequired(String destinationType, String fbaWarehouseCode,
|
||||
boolean allowDeferredForRelabel) {
|
||||
String type = trim(destinationType);
|
||||
if (type == null && StringUtils.hasText(fbaWarehouseCode)) type = "FBA";
|
||||
if ((type == null || "NONE".equalsIgnoreCase(type)) && !allowDeferredForRelabel) {
|
||||
throw badRequest("仓储转运必须选择 FBA 仓或私人仓");
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyDestination(WarehouseTransferOrder transfer, WarehouseTransferRequests.Create request) {
|
||||
String type = trim(request.destinationType());
|
||||
if (type == null && StringUtils.hasText(request.fbaWarehouseCode())) type = "FBA";
|
||||
|
||||
@@ -1628,6 +1628,10 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
|
||||
PREPARE transfer_outbound_hash_stmt FROM @ddl; EXECUTE transfer_outbound_hash_stmt; DEALLOCATE PREPARE transfer_outbound_hash_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_at DATETIME(3) NULL COMMENT ''出库时间'' AFTER outbound_request_hash'));
|
||||
PREPARE transfer_outbound_at_stmt FROM @ddl; EXECUTE transfer_outbound_at_stmt; DEALLOCATE PREPARE transfer_outbound_at_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='measured_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN measured_at DATETIME(3) NULL COMMENT ''PDA测量完成时间'' AFTER outbound_at'));
|
||||
PREPARE transfer_measured_at_stmt FROM @ddl; EXECUTE transfer_measured_at_stmt; DEALLOCATE PREPARE transfer_measured_at_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='actual_ship_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN actual_ship_at DATETIME(3) NULL COMMENT ''实际发货时间'' AFTER measured_at'));
|
||||
PREPARE transfer_actual_ship_at_stmt FROM @ddl; EXECUTE transfer_actual_ship_at_stmt; DEALLOCATE PREPARE transfer_actual_ship_at_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='estimated_ship_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN estimated_ship_at DATETIME(3) NULL COMMENT ''预计发货时间'' AFTER estimated_arrival_at'));
|
||||
PREPARE transfer_estimated_ship_stmt FROM @ddl; EXECUTE transfer_estimated_ship_stmt; DEALLOCATE PREPARE transfer_estimated_ship_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='pickup_contact_name'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN pickup_contact_name VARCHAR(128) NULL COMMENT ''提货联系人'' AFTER delivery_address'));
|
||||
@@ -2056,7 +2060,7 @@ CREATE TABLE IF NOT EXISTS ll_dropship_carton_ledger (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_cargo_discard_application (
|
||||
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
discard_no VARCHAR(64) NOT NULL COMMENT '废弃申请单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '海外仓ID',
|
||||
discard_no VARCHAR(64) NOT NULL COMMENT '废弃申请单号', outbound_no VARCHAR(64) NULL COMMENT '废弃出库单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '海外仓ID',
|
||||
overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照', inbound_codes VARCHAR(2000) NOT NULL COMMENT '入仓单号集合',
|
||||
total_packages INT NOT NULL COMMENT '总箱数', fee_per_package DECIMAL(18,2) NOT NULL COMMENT '每箱处理费用',
|
||||
total_fee DECIMAL(18,2) NOT NULL COMMENT '费用合计', currency CHAR(3) NOT NULL DEFAULT 'USD' COMMENT '币种',
|
||||
@@ -2064,7 +2068,7 @@ CREATE TABLE IF NOT EXISTS ll_cargo_discard_application (
|
||||
idempotency_key VARCHAR(255) NOT NULL COMMENT '业务幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求内容哈希',
|
||||
created_by VARCHAR(128) NULL COMMENT '创建人ID', reviewed_by VARCHAR(128) NULL COMMENT '审核人ID', review_remark VARCHAR(1000) NULL COMMENT '审核备注',
|
||||
review_idempotency_key VARCHAR(255) NULL COMMENT '审核幂等键', review_request_hash CHAR(64) NULL COMMENT '审核请求内容哈希',
|
||||
reviewed_at DATETIME(3) NULL COMMENT '审核时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||
reviewed_at DATETIME(3) NULL COMMENT '审核时间', completed_at DATETIME(3) NULL COMMENT '废弃完成时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_ll_cargo_discard_no(customer_id,discard_no),
|
||||
UNIQUE KEY uk_ll_cargo_discard_key(customer_id,idempotency_key),
|
||||
|
||||
@@ -1556,6 +1556,13 @@ paths:
|
||||
security: [{ bearerAuth: [] }]
|
||||
parameters: [{ name: returnNo, in: path, required: true, schema: { type: string } }]
|
||||
responses: { '200': { description: 当前客户退货订单详情 }, '404': { description: 当前客户下退货订单不存在 } }
|
||||
put:
|
||||
summary: 客户编辑待审核退货预报
|
||||
description: 权限 merchant:overseas-warehouse:return-order:create;仅允许修改当前客户且仍处于 PENDING_REVIEW 的退货预报,SKU 预报明细会整体替换。
|
||||
security: [{ bearerAuth: [] }]
|
||||
parameters: [{ name: returnNo, in: path, required: true, schema: { type: string } }]
|
||||
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ReturnOrderCreate' } } } }
|
||||
responses: { '200': { description: 更新后的退货订单详情 }, '404': { description: 当前客户下退货订单不存在 }, '409': { description: 退货预报已不处于待审核状态 } }
|
||||
/api/merchant/v1/last-leg/return-orders/{returnNo}/photos/files/{fileId}/download:
|
||||
get:
|
||||
summary: 客户下载退货签收打托照片
|
||||
|
||||
+1
@@ -85,6 +85,7 @@ class MerchantOverseasCustomerApiContractTest {
|
||||
"cancel", OverseasPermissions.MERCHANT_DISCARD_CANCEL));
|
||||
assertPermissions(MerchantReturnOrderController.class, Map.of(
|
||||
"create", OverseasPermissions.MERCHANT_RETURN_ORDER_CREATE,
|
||||
"update", OverseasPermissions.MERCHANT_RETURN_ORDER_CREATE,
|
||||
"page", OverseasPermissions.MERCHANT_RETURN_ORDER_LIST,
|
||||
"detail", OverseasPermissions.MERCHANT_RETURN_ORDER_LIST,
|
||||
"downloadPhoto", OverseasPermissions.MERCHANT_RETURN_ORDER_LIST,
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ class MerchantRelabelApplicationControllerTest {
|
||||
private final RelabelApplicationService service = mock(RelabelApplicationService.class);
|
||||
private final MerchantRelabelApplicationController controller = controller();
|
||||
private final RelabelRequests.Query query = new RelabelRequests.Query(
|
||||
1, 20, null, null, null, null, null, null, null);
|
||||
1, 20, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null);
|
||||
|
||||
@Test
|
||||
void merchantPageAndDetailUseOnlyAuthenticatedTenant() {
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ class MerchantWarehouseTransferControllerTest {
|
||||
private final WarehouseTransferService service = mock(WarehouseTransferService.class);
|
||||
private final MerchantWarehouseTransferController controller = controller();
|
||||
private final WarehouseTransferRequests.Query query = new WarehouseTransferRequests.Query(
|
||||
1, 20, null, null, null, null, null, null, null, null);
|
||||
1, 20, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null);
|
||||
|
||||
@Test
|
||||
void merchantPageUsesOnlyAuthenticatedTenant() {
|
||||
|
||||
+61
-7
@@ -120,7 +120,8 @@ class WarehouseTransferServiceTest {
|
||||
when(relabelApplications.selectList(any())).thenReturn(List.of(relabel));
|
||||
|
||||
var result = service.page("TENANT-A", new WarehouseTransferRequests.Query(
|
||||
1, 20, null, null, null, null, null, null, null, null));
|
||||
1, 20, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null));
|
||||
|
||||
assertEquals("RL-20260923-000001", result.records().get(0).relabelNo());
|
||||
assertEquals("COMPLETED", result.records().get(0).relabelApplicationStatus());
|
||||
@@ -387,19 +388,25 @@ class WarehouseTransferServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transferRejectsRequestWithoutDestination() {
|
||||
void transferAllowsRequestWithoutDestinationForLaterBinding() {
|
||||
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
|
||||
"IN-1", List.of(11L), "WH-1", null, "123 Pickup Road", 5,
|
||||
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
|
||||
null, null, null, "FBA destination will be supplied later", false, "key-no-fba",
|
||||
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")));
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> service.create("TENANT-A", "USER-1", request));
|
||||
when(inboundSnapshots.findId("TENANT-A", "IN-1")).thenReturn(101L);
|
||||
when(orders.selectList(any())).thenReturn(List.of(historicalOrder()));
|
||||
when(transfers.findNo(eq("TENANT-A"), anyString())).thenAnswer(invocation -> transfer(WarehouseTransferService.DRAFT));
|
||||
when(transferItems.selectList(any())).thenReturn(List.of());
|
||||
when(sourceOrders.selectList(any())).thenReturn(List.of());
|
||||
|
||||
assertEquals(400, exception.getStatusCode().value());
|
||||
assertEquals("仓储转运必须选择 FBA 仓或私人仓", exception.getReason());
|
||||
verifyNoInteractions(transfers);
|
||||
var result = service.create("TENANT-A", "USER-1", request);
|
||||
|
||||
assertEquals(WarehouseTransferService.DRAFT, result.status());
|
||||
ArgumentCaptor<WarehouseTransferOrder> transfer = ArgumentCaptor.forClass(WarehouseTransferOrder.class);
|
||||
verify(transfers).insert(transfer.capture());
|
||||
assertNull(transfer.getValue().getDestinationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -611,6 +618,53 @@ class WarehouseTransferServiceTest {
|
||||
verify(orderDetails).insert(any(LastlegOrderDetail.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantUpdatesRejectedTransferBeforeResubmission() {
|
||||
WarehouseTransferOrder transfer = persistedTransfer();
|
||||
transfer.setStatus(WarehouseTransferService.REJECTED);
|
||||
LastlegOrder tailOrder = historicalOrder();
|
||||
tailOrder.setId(31L); tailOrder.setCustomerId("TENANT-A"); tailOrder.setCurrentStatus("CANCELLED");
|
||||
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||
when(orders.selectById(31L)).thenReturn(tailOrder);
|
||||
when(transferItems.selectList(any())).thenReturn(List.of());
|
||||
when(sourceOrders.selectList(any())).thenReturn(List.of());
|
||||
|
||||
var result = service.updateDraft("TENANT-A", "WTR-1", "USER-1", request(false));
|
||||
|
||||
assertEquals(WarehouseTransferService.REJECTED, result.status());
|
||||
assertTrue(tailOrder.getSubOrderStatus().startsWith("WAREHOUSE_TRANSFER:REJECTED"));
|
||||
verify(transfers).updateById(transfer);
|
||||
verify(orders).updateById(tailOrder);
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantResubmitsRejectedTransferForReview() {
|
||||
WarehouseTransferOrder transfer = persistedTransfer();
|
||||
transfer.setStatus(WarehouseTransferService.REJECTED);
|
||||
transfer.setInboundCode("WTR-1");
|
||||
LastlegOrder tailOrder = historicalOrder();
|
||||
tailOrder.setId(31L); tailOrder.setCustomerId("TENANT-A"); tailOrder.setCurrentStatus("CANCELLED");
|
||||
tailOrder.setRelabelStatus("NOT_REQUIRED");
|
||||
LastlegInboundOrder inbound = new LastlegInboundOrder();
|
||||
inbound.setId(101L); inbound.setCustomerId("TENANT-A"); inbound.setStatus(WarehouseTransferService.PENDING_REVIEW);
|
||||
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||
when(orders.selectById(31L)).thenReturn(tailOrder);
|
||||
when(inboundOrders.selectById(101L)).thenReturn(inbound);
|
||||
when(transferItems.selectList(any())).thenReturn(List.of());
|
||||
when(sourceOrders.selectList(any())).thenReturn(List.of());
|
||||
|
||||
var result = service.submit("TENANT-A", "WTR-1", "USER-1");
|
||||
|
||||
assertEquals(WarehouseTransferService.PENDING_REVIEW, result.status());
|
||||
assertEquals(WarehouseTransferService.PENDING_REVIEW, tailOrder.getCurrentStatus());
|
||||
var history = ArgumentCaptor.forClass(com.baiyue.lastleg.entity.LastlegOrderStatusHistory.class);
|
||||
verify(orderHistories).insert(history.capture());
|
||||
assertEquals(WarehouseTransferService.REJECTED, history.getValue().getFromStatus());
|
||||
assertEquals(WarehouseTransferService.PENDING_REVIEW, history.getValue().getToStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantCannotUpdateSubmittedTransfer() {
|
||||
WarehouseTransferOrder transfer = persistedTransfer();
|
||||
|
||||
@@ -1369,7 +1369,7 @@ USE baiyue_lastleg;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_cargo_discard_application (
|
||||
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
discard_no VARCHAR(64) NOT NULL COMMENT '废弃申请单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '海外仓ID',
|
||||
discard_no VARCHAR(64) NOT NULL COMMENT '废弃申请单号', outbound_no VARCHAR(64) NULL COMMENT '废弃出库单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '海外仓ID',
|
||||
overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照', inbound_codes VARCHAR(2000) NOT NULL COMMENT '入仓单号集合',
|
||||
total_packages INT NOT NULL COMMENT '总箱数', fee_per_package DECIMAL(18,2) NOT NULL COMMENT '每箱处理费用',
|
||||
total_fee DECIMAL(18,2) NOT NULL COMMENT '费用合计', currency CHAR(3) NOT NULL DEFAULT 'USD' COMMENT '币种',
|
||||
@@ -1493,7 +1493,11 @@ PREPARE transfer_inventory_024 FROM @ddl; EXECUTE transfer_inventory_024; DEALLO
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_request_hash'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_request_hash CHAR(64) NULL COMMENT ''出库请求哈希'' AFTER outbound_idempotency_key'));
|
||||
PREPARE transfer_inventory_024 FROM @ddl; EXECUTE transfer_inventory_024; DEALLOCATE PREPARE transfer_inventory_024;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='outbound_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN outbound_at DATETIME(3) NULL COMMENT ''出库时间'' AFTER outbound_request_hash'));
|
||||
PREPARE transfer_inventory_024 FROM @ddl; EXECUTE transfer_inventory_024; DEALLOCATE PREPARE transfer_inventory_024;
|
||||
PREPARE transfer_outbound_at_stmt FROM @ddl; EXECUTE transfer_outbound_at_stmt; DEALLOCATE PREPARE transfer_outbound_at_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='measured_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN measured_at DATETIME(3) NULL COMMENT ''PDA测量完成时间'' AFTER outbound_at'));
|
||||
PREPARE transfer_measured_at_stmt FROM @ddl; EXECUTE transfer_measured_at_stmt; DEALLOCATE PREPARE transfer_measured_at_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='actual_ship_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN actual_ship_at DATETIME(3) NULL COMMENT ''实际发货时间'' AFTER measured_at'));
|
||||
PREPARE transfer_actual_ship_at_stmt FROM @ddl; EXECUTE transfer_actual_ship_at_stmt; DEALLOCATE PREPARE transfer_actual_ship_at_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_inventory_allocation (
|
||||
id BIGINT NOT NULL COMMENT '雪花主键', transfer_order_id BIGINT NOT NULL COMMENT '转仓单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
@@ -4304,3 +4308,5 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
|
||||
PREPARE dispatch_postal_stmt FROM @ddl; EXECUTE dispatch_postal_stmt; DEALLOCATE PREPARE dispatch_postal_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='postal_code'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编'' AFTER delivery_address'));
|
||||
PREPARE delivery_postal_stmt FROM @ddl; EXECUTE delivery_postal_stmt; DEALLOCATE PREPARE delivery_postal_stmt;
|
||||
SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_cargo_discard_application' AND column_name='outbound_no'), 'ALTER TABLE ll_cargo_discard_application ADD COLUMN outbound_no VARCHAR(64) NULL COMMENT ''废弃出库单号'' AFTER discard_no', 'SELECT 1');
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
Reference in New Issue
Block a user