优化
This commit is contained in:
+1
-1
@@ -76,7 +76,7 @@ public class PlatformContainerFeeDetailController {
|
||||
@PathVariable long loadingPlanId, @PathVariable long feeId,
|
||||
@Valid @RequestBody AdjustRequest request) {
|
||||
return ApiResponse.success(service.adjust(FirstlegScopeGuard.platform(user, accountId),
|
||||
loadingPlanId, feeId, request.expectedSourceVersion(), user.userId(), request.values()));
|
||||
loadingPlanId, feeId, user.userId(), request));
|
||||
}
|
||||
|
||||
@PostMapping("/{feeId}/void")
|
||||
|
||||
@@ -29,20 +29,9 @@ public final class ContainerFeeDetailDtos {
|
||||
|
||||
public record AdjustRequest(
|
||||
@NotNull @Min(1) Integer expectedSourceVersion,
|
||||
@NotNull @Min(1) Long feeItemId,
|
||||
@NotBlank @Pattern(regexp = "RECEIVABLE|PAYABLE") String direction,
|
||||
@NotNull LocalDate businessDate,
|
||||
@NotNull @DecimalMin("0.00") BigDecimal amount,
|
||||
@NotBlank @Pattern(regexp = "CNY") String currency,
|
||||
@NotNull Boolean settlementIncluded,
|
||||
Long partnerId,
|
||||
@Size(max = 200) String partnerName,
|
||||
@Size(max = 500) String remark
|
||||
@NotNull @DecimalMin("0.00") BigDecimal amount
|
||||
) {
|
||||
public SaveRequest values() {
|
||||
return new SaveRequest(feeItemId, direction, businessDate, amount, currency,
|
||||
settlementIncluded, partnerId, partnerName, remark);
|
||||
}
|
||||
}
|
||||
|
||||
public record VersionRequest(@NotNull @Min(1) Integer expectedSourceVersion) {
|
||||
|
||||
@@ -10,17 +10,19 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baiyue.firstl
|
||||
""")
|
||||
int removeEditing(@Param("planId") long planId,@Param("itemId") long itemId,@Param("expectedVersion") int expectedVersion,@Param("operatorId") long operatorId,@Param("operatorName") String operatorName);
|
||||
@Update("""
|
||||
UPDATE fl_loading_plan_item i JOIN fl_loading_plan p ON p.id=i.loading_plan_id
|
||||
UPDATE fl_loading_plan_item i
|
||||
SET i.warehouse_remark=#{remark}, i.updated_by=#{operatorId}, i.updated_by_name=#{operatorName},
|
||||
i.updated_at=CURRENT_TIMESTAMP(3), i.version=i.version+1,
|
||||
p.version=p.version+1, p.updated_by=#{operatorId}, p.updated_by_name=#{operatorName},
|
||||
p.updated_at=CURRENT_TIMESTAMP(3)
|
||||
WHERE i.id=#{itemId} AND i.loading_plan_id=#{planId} AND i.deleted=0
|
||||
AND i.version=#{expectedItemVersion} AND p.version=#{expectedPlanVersion}
|
||||
AND p.plan_status IN ('EDITING','LOADING')
|
||||
i.updated_at=CURRENT_TIMESTAMP(3), i.version=i.version+1
|
||||
WHERE i.id=#{itemId} AND i.loading_plan_id=#{planId} AND i.deleted=0 AND i.frozen_at IS NULL
|
||||
AND i.version=#{expectedItemVersion}
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan p
|
||||
WHERE p.id=#{planId} AND p.version=#{touchedPlanVersion}
|
||||
AND p.plan_status IN ('EDITING','LOADING')
|
||||
)
|
||||
""")
|
||||
int updateWarehouseRemark(@Param("planId") long planId, @Param("itemId") long itemId,
|
||||
@Param("expectedPlanVersion") int expectedPlanVersion,
|
||||
@Param("touchedPlanVersion") int touchedPlanVersion,
|
||||
@Param("expectedItemVersion") int expectedItemVersion,
|
||||
@Param("remark") String remark, @Param("operatorId") long operatorId,
|
||||
@Param("operatorName") String operatorName);
|
||||
|
||||
+20
-6
@@ -7,6 +7,8 @@ import com.baiyue.firstleg.mapper.ContainerFeeDetailMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -56,14 +58,26 @@ public class ContainerFeeDetailRepository {
|
||||
.set(ContainerFeeDetailEntity::getUpdatedBy, reviewerId));
|
||||
}
|
||||
|
||||
public int adjust(ContainerFeeDetailEntity value, int expectedVersion) {
|
||||
return mapper.update(value, new LambdaUpdateWrapper<ContainerFeeDetailEntity>()
|
||||
.eq(ContainerFeeDetailEntity::getTenantId, value.getTenantId())
|
||||
.eq(ContainerFeeDetailEntity::getLoadingPlanId, value.getLoadingPlanId())
|
||||
.eq(ContainerFeeDetailEntity::getId, value.getId())
|
||||
public int adjust(long tenantId, long loadingPlanId, long feeId, int expectedVersion,
|
||||
LocalDate businessDate, BigDecimal amount, long operatorId,
|
||||
LocalDateTime updatedAt) {
|
||||
return mapper.update(null, new LambdaUpdateWrapper<ContainerFeeDetailEntity>()
|
||||
.eq(ContainerFeeDetailEntity::getTenantId, tenantId)
|
||||
.eq(ContainerFeeDetailEntity::getLoadingPlanId, loadingPlanId)
|
||||
.eq(ContainerFeeDetailEntity::getId, feeId)
|
||||
.eq(ContainerFeeDetailEntity::getReviewStatus, "APPROVED")
|
||||
.eq(ContainerFeeDetailEntity::getSourceStatus, "ACTIVE")
|
||||
.eq(ContainerFeeDetailEntity::getSourceVersion, expectedVersion));
|
||||
.eq(ContainerFeeDetailEntity::getSourceVersion, expectedVersion)
|
||||
.set(ContainerFeeDetailEntity::getBusinessDate, businessDate)
|
||||
.set(ContainerFeeDetailEntity::getAmount, amount)
|
||||
.set(ContainerFeeDetailEntity::getSourceVersion, expectedVersion + 1)
|
||||
.set(ContainerFeeDetailEntity::getReviewStatus, "PENDING")
|
||||
.set(ContainerFeeDetailEntity::getReviewerId, null)
|
||||
.set(ContainerFeeDetailEntity::getReviewerName, null)
|
||||
.set(ContainerFeeDetailEntity::getReviewedAt, null)
|
||||
.set(ContainerFeeDetailEntity::getReviewReason, null)
|
||||
.set(ContainerFeeDetailEntity::getUpdatedBy, operatorId)
|
||||
.set(ContainerFeeDetailEntity::getUpdatedAt, updatedAt));
|
||||
}
|
||||
|
||||
public int voidApproved(long tenantId, long loadingPlanId, long feeId,
|
||||
|
||||
@@ -69,9 +69,9 @@ public class SmartLoadingRepository {
|
||||
public int remove(long planId, long itemId, int version, long operatorId, String operatorName) {
|
||||
return items.removeEditing(planId, itemId, version, operatorId, operatorName);
|
||||
}
|
||||
public int updateWarehouseRemark(long planId, long itemId, int expectedPlanVersion, int expectedItemVersion,
|
||||
public int updateWarehouseRemark(long planId, long itemId, int touchedPlanVersion, int expectedItemVersion,
|
||||
String remark, long operatorId, String operatorName) {
|
||||
return items.updateWarehouseRemark(planId, itemId, expectedPlanVersion, expectedItemVersion,
|
||||
return items.updateWarehouseRemark(planId, itemId, touchedPlanVersion, expectedItemVersion,
|
||||
remark, operatorId, operatorName);
|
||||
}
|
||||
public int push(long planId, String key, int version, long operatorId, String operatorName) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos.AdjustRequest;
|
||||
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos.SaveRequest;
|
||||
import com.baiyue.firstleg.entity.ContainerBookingEntity;
|
||||
import com.baiyue.firstleg.entity.ContainerFeeDetailEntity;
|
||||
@@ -81,19 +82,23 @@ public class ContainerFeeDetailService {
|
||||
|
||||
@Transactional
|
||||
public ContainerFeeDetailEntity adjust(long tenantId, long loadingPlanId, long feeId,
|
||||
int expectedVersion, long operatorId,
|
||||
SaveRequest request) {
|
||||
long operatorId, AdjustRequest request) {
|
||||
requireOwnedPlan(tenantId, loadingPlanId);
|
||||
ContainerFeeDetailEntity value = required(tenantId, loadingPlanId, feeId);
|
||||
FeeItemEntity item = requireItem(request.feeItemId(), request.direction());
|
||||
apply(value, item, request, operatorId);
|
||||
value.setSourceVersion(expectedVersion + 1);
|
||||
value.setReviewStatus("PENDING");
|
||||
value.setReviewerId(null);
|
||||
value.setReviewerName(null);
|
||||
value.setReviewedAt(null);
|
||||
value.setReviewReason(null);
|
||||
if (repository.adjust(value, expectedVersion) != 1) {
|
||||
int expectedVersion = request.expectedSourceVersion();
|
||||
var amount = request.amount().setScale(2, RoundingMode.HALF_UP);
|
||||
if (value.getSourceVersion() != null && value.getSourceVersion() == expectedVersion + 1
|
||||
&& "ACTIVE".equals(value.getSourceStatus()) && "PENDING".equals(value.getReviewStatus())
|
||||
&& request.businessDate().equals(value.getBusinessDate())
|
||||
&& amount.compareTo(value.getAmount()) == 0) {
|
||||
return value;
|
||||
}
|
||||
if (!"ACTIVE".equals(value.getSourceStatus()) || !"APPROVED".equals(value.getReviewStatus())
|
||||
|| value.getSourceVersion() == null || value.getSourceVersion() != expectedVersion) {
|
||||
throw conflict("只有当前版本的已审核柜次费用可以调整");
|
||||
}
|
||||
if (repository.adjust(tenantId, loadingPlanId, feeId, expectedVersion,
|
||||
request.businessDate(), amount, operatorId, LocalDateTime.now()) != 1) {
|
||||
throw conflict("只有当前版本的已审核柜次费用可以调整");
|
||||
}
|
||||
return required(tenantId, loadingPlanId, feeId);
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService;
|
||||
import com.baiyue.firstleg.split.service.SplitPreloadService;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -25,6 +26,7 @@ import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@@ -228,9 +230,17 @@ public class SmartLoadingService {
|
||||
LoadingPlanItemEntity item = repository.item(planId, itemId);
|
||||
if (item == null) throw new BizException(ResultCode.NOT_FOUND, "装柜明细不存在");
|
||||
String before = json(values("warehouseRemark", item.getWarehouseRemark(), "version", item.getVersion()));
|
||||
if (repository.updateWarehouseRemark(planId, itemId, request.expectedPlanVersion(),
|
||||
request.expectedItemVersion(), remark, operatorId, operatorName) != 1) {
|
||||
throw conflict("装柜计划或明细已被其他人修改,请刷新后重试");
|
||||
try {
|
||||
touch(plan, operatorId, operatorName);
|
||||
if (repository.updateWarehouseRemark(planId, itemId, request.expectedPlanVersion() + 1,
|
||||
request.expectedItemVersion(), remark, operatorId, operatorName) != 1) {
|
||||
throw conflict("装柜计划或明细已被其他人修改,请刷新后重试");
|
||||
}
|
||||
} catch (DataAccessException exception) {
|
||||
if (hasSqlState(exception, "45000")) {
|
||||
throw conflict("装柜计划或明细状态已变化,仓库备注未保存,请刷新后重试");
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
history(planId, itemId, "WAREHOUSE_REMARK", plan.getPlanStatus(), plan.getPlanStatus(), operation,
|
||||
before, json(values("warehouseRemark", remark, "requestHash", requestHash)), operatorId, operatorName);
|
||||
@@ -535,6 +545,15 @@ public class SmartLoadingService {
|
||||
return new BizException(ResultCode.SERVICE_UNAVAILABLE, "订单货物服务暂不可用");
|
||||
}
|
||||
|
||||
private static boolean hasSqlState(Throwable throwable, String sqlState) {
|
||||
for (Throwable current = throwable; current != null; current = current.getCause()) {
|
||||
if (current instanceof SQLException sqlException && sqlState.equals(sqlException.getSQLState())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String trim(String x){return x==null?null:x.trim();} private static BigDecimal unit(BigDecimal x){return x==null?BigDecimal.ZERO:x.max(BigDecimal.ZERO);}
|
||||
private static void requireAccount(long x){if(x<=0)throw new BizException(ResultCode.BAD_REQUEST,"accountId必须大于零");} private static BizException conflict(String x){return new BizException(ResultCode.CONFLICT,x);} private static BizException validation(String x){return new BizException(ResultCode.VALIDATION_FAILED,x);} private static BizException forbidden(){return new BizException(ResultCode.FORBIDDEN,"客户归属不匹配");}
|
||||
}
|
||||
|
||||
@@ -827,7 +827,44 @@ BEGIN
|
||||
IF current_plan_status = 'LOADING' AND EXISTS (
|
||||
SELECT 1 FROM fl_loading_pick_item
|
||||
WHERE loading_plan_item_id = OLD.id AND picked_package_count > 0) THEN
|
||||
IF NOT EXISTS (
|
||||
IF NOT (NEW.warehouse_remark <=> OLD.warehouse_remark) THEN
|
||||
IF NEW.package_count <> OLD.package_count OR NEW.deleted <> OLD.deleted
|
||||
OR NOT (NEW.customer_name <=> OLD.customer_name)
|
||||
OR NOT (NEW.inbound_id <=> OLD.inbound_id)
|
||||
OR NOT (NEW.inbound_detail_id <=> OLD.inbound_detail_id)
|
||||
OR NOT (NEW.order_id <=> OLD.order_id)
|
||||
OR NOT (NEW.order_no <=> OLD.order_no)
|
||||
OR NOT (NEW.inbound_code <=> OLD.inbound_code)
|
||||
OR NOT (NEW.destination_warehouse_id <=> OLD.destination_warehouse_id)
|
||||
OR NOT (NEW.destination_warehouse_code <=> OLD.destination_warehouse_code)
|
||||
OR NOT (NEW.destination_warehouse_name <=> OLD.destination_warehouse_name)
|
||||
OR NOT (NEW.sku_code <=> OLD.sku_code)
|
||||
OR NOT (NEW.product_name_zh <=> OLD.product_name_zh)
|
||||
OR NOT (NEW.product_name_en <=> OLD.product_name_en)
|
||||
OR NOT (NEW.product_image_file_id <=> OLD.product_image_file_id)
|
||||
OR NOT (NEW.product_image_url <=> OLD.product_image_url)
|
||||
OR NOT (NEW.product_quantity <=> OLD.product_quantity)
|
||||
OR NOT (NEW.units_per_carton <=> OLD.units_per_carton)
|
||||
OR NOT (NEW.pallet_count <=> OLD.pallet_count)
|
||||
OR NOT (NEW.gross_weight_kg <=> OLD.gross_weight_kg)
|
||||
OR NOT (NEW.net_weight_kg <=> OLD.net_weight_kg)
|
||||
OR NOT (NEW.dimensional_weight_kg <=> OLD.dimensional_weight_kg)
|
||||
OR NOT (NEW.volume_cbm <=> OLD.volume_cbm)
|
||||
OR NOT (NEW.fba_tracking_no <=> OLD.fba_tracking_no)
|
||||
OR NOT (NEW.shipment_no <=> OLD.shipment_no)
|
||||
OR NOT (NEW.shipping_mark <=> OLD.shipping_mark)
|
||||
OR NOT (NEW.load_sequence <=> OLD.load_sequence)
|
||||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||||
OR NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||||
OR NOT (NEW.removed_by_name <=> OLD.removed_by_name)
|
||||
OR NOT (NEW.removed_at <=> OLD.removed_at) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有PDA拣货记录的装柜明细修改仓库备注时不得变更其他业务字段';
|
||||
END IF;
|
||||
ELSEIF NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_pick_item
|
||||
WHERE loading_plan_item_id = OLD.id
|
||||
AND item_status = 'PARTIAL'
|
||||
@@ -862,7 +899,6 @@ BEGIN
|
||||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||||
OR NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||||
OR NOT (NEW.warehouse_remark <=> OLD.warehouse_remark)
|
||||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||||
|
||||
@@ -833,6 +833,26 @@ paths:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/MerchantInboundBillingEnvelope'}
|
||||
'403': {description: 非当前客户或无订单查看权限}
|
||||
/api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/adjust:
|
||||
post:
|
||||
summary: 调整已审核柜次费用的日期和金额
|
||||
description: >-
|
||||
仅 ACTIVE、APPROVED 且 sourceVersion 匹配的费用可调整。成功后 sourceVersion 加一并重新进入
|
||||
PENDING;费用方向、费用项目、往来单位、币种、参与结算和备注保持原快照。身份或结算字段需要变化时,
|
||||
必须作废原费用并新增。同一 expectedSourceVersion、businessDate、amount 的原样重试只读返回已生成的
|
||||
PENDING 版本;旧版本异参、已作废或非已审核状态返回 A0409 且零写入。
|
||||
parameters:
|
||||
- {name: accountId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: loadingPlanId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: feeId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ContainerFeeAdjustRequest'}
|
||||
responses:
|
||||
'200': {description: 成功时 data 为调整后柜次费用;业务冲突通过响应体 A0409 返回}
|
||||
'403': {description: 无 firstleg:inbound:fee-detail:edit 权限}
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
@@ -1793,3 +1813,11 @@ components:
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
reason: {type: string, minLength: 1, maxLength: 500}
|
||||
ContainerFeeAdjustRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [expectedSourceVersion, businessDate, amount]
|
||||
properties:
|
||||
expectedSourceVersion: {type: integer, minimum: 1}
|
||||
businessDate: {type: string, format: date}
|
||||
amount: {type: number, minimum: 0}
|
||||
|
||||
Reference in New Issue
Block a user