v1.0.31 #43
@@ -111,14 +111,6 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
||||
|
||||
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_CREATE("PACKAGE_DELIVERY:CREATE", "套餐发放创建", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_READ("PACKAGE_DELIVERY:READ", "套餐发放读取", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_VERIFY("PACKAGE_DELIVERY:VERIFY", "套餐发放核销", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_REVOKE("PACKAGE_DELIVERY:REVOKE", "套餐发放冲正", true, true),
|
||||
|
||||
FUND_RECORD_READ("FUND_RECORD:READ", "资金流水读取", true, true),
|
||||
|
||||
FUND_RECEIVE_CREATE("FUND_RECEIVE:CREATE", "线下收款创建", true, true),
|
||||
|
||||
+32
-59
@@ -6,12 +6,11 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillment;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
@@ -29,7 +28,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -45,6 +43,7 @@ public class GaoCustomerPackageController {
|
||||
private final GaoCustomerPackageService service;
|
||||
private final GaoCustomerService customerService;
|
||||
private final GaoOrderFulfillmentService fulfillmentService;
|
||||
private final GaoSalesOrderService salesOrderService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserService userService;
|
||||
@@ -57,23 +56,7 @@ public class GaoCustomerPackageController {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoCustomerPackage> result = service.pageByQuery(page);
|
||||
if (result.getList().isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
List<String> customerIdList = result.getList().stream()
|
||||
.map(GaoCustomerPackage::getCustomerId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, GaoCustomer> customerMap = customerService.mapByIdList(customerIdList);
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name());
|
||||
for (GaoCustomerPackage packageEntity : result.getList()) {
|
||||
GaoCustomer customer = customerMap.get(packageEntity.getCustomerId());
|
||||
if (customer != null) {
|
||||
customer.setAttachmentList(attachmentMap.get(customer.getId()));
|
||||
}
|
||||
packageEntity.setCustomer(customer);
|
||||
}
|
||||
fillTransient(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,52 +102,42 @@ public class GaoCustomerPackageController {
|
||||
if (packageList == null || packageList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<GaoCustomerPackageItem>> itemMap = service.mapItemListByCustomerPackageIdList(packageList.stream().map(GaoCustomerPackage::getId).toList());
|
||||
Map<String, GaoSalesOrder> orderMap = salesOrderService.mapByIdList(packageList.stream()
|
||||
.map(GaoCustomerPackage::getSalesOrderId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList());
|
||||
List<String> customerIdList = packageList.stream()
|
||||
.map(GaoCustomerPackage::getCustomerId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, GaoCustomer> customerMap = customerIdList.isEmpty() ? Map.of() : customerService.mapByIdList(customerIdList);
|
||||
Map<String, List<Attachment>> attachmentMap = customerIdList.isEmpty()
|
||||
? Map.of() : attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name());
|
||||
List<String> userIdList = packageList.stream()
|
||||
.map(GaoCustomerPackage::getCancelOperatorUserId)
|
||||
.map(packageEntity -> orderMap.get(packageEntity.getSalesOrderId()))
|
||||
.filter(order -> order != null)
|
||||
.map(GaoSalesOrder::getCancelOperatorUserId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoCustomerPackage packageEntity : packageList) {
|
||||
List<GaoCustomerPackageItem> itemList = itemMap.getOrDefault(packageEntity.getId(), List.of());
|
||||
fillItemAttachments(itemList);
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
fillDerived(item);
|
||||
GaoSalesOrder order = orderMap.get(packageEntity.getSalesOrderId());
|
||||
packageEntity.setSalesOrder(order);
|
||||
packageEntity.setStatus(order == null ? null : order.getStatus());
|
||||
packageEntity.setCancelledAt(order == null ? null : order.getCancelledAt());
|
||||
packageEntity.setCancelOperatorUserId(order == null ? null : order.getCancelOperatorUserId());
|
||||
packageEntity.setCancelReason(order == null ? null : order.getCancelReason());
|
||||
packageEntity.setItemList(order == null ? List.of() : order.getItemList());
|
||||
GaoCustomer customer = customerMap.get(packageEntity.getCustomerId());
|
||||
if (customer != null) {
|
||||
customer.setAttachmentList(attachmentMap.getOrDefault(customer.getId(), List.of()));
|
||||
}
|
||||
packageEntity.setItemList(itemList);
|
||||
String cancelOperatorUserId = packageEntity.getCancelOperatorUserId();
|
||||
packageEntity.setCustomer(customer);
|
||||
String cancelOperatorUserId = order == null ? null : order.getCancelOperatorUserId();
|
||||
packageEntity.setCancelOperator(cancelOperatorUserId == null ? null : userMap.get(cancelOperatorUserId));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillItemAttachments(List<GaoCustomerPackageItem> itemList) {
|
||||
if (itemList == null || itemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> productIdList = itemList.stream()
|
||||
.map(GaoCustomerPackageItem::getProductId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
if (productIdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
||||
Attachment.BizType.GAO_PRODUCT,
|
||||
productIdList,
|
||||
GaoProduct.AttachType.PHOTO.name());
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDerived(GaoCustomerPackageItem item) {
|
||||
BigDecimal reserved = item.getReservedQuantity() == null ? BigDecimal.ZERO : item.getReservedQuantity();
|
||||
BigDecimal fulfilled = item.getFulfilledQuantity() == null ? BigDecimal.ZERO : item.getFulfilledQuantity();
|
||||
BigDecimal total = item.getTotalQuantity() == null ? BigDecimal.ZERO : item.getTotalQuantity();
|
||||
item.setPendingQuantity(reserved);
|
||||
item.setAvailableQuantity(total.subtract(reserved).subtract(fulfilled));
|
||||
item.setRemainingQuantity(total.subtract(fulfilled));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,6 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoCustomerPackage extends UUIDEntity {
|
||||
|
||||
/// 客户套餐状态
|
||||
public enum Status {
|
||||
|
||||
/// 履约中
|
||||
ACTIVE,
|
||||
|
||||
/// 已全部履约
|
||||
FULFILLED,
|
||||
|
||||
/// 已取消
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
@@ -47,16 +34,20 @@ public class GaoCustomerPackage extends UUIDEntity {
|
||||
/// 标准价快照,单位分
|
||||
private Long standardAmountSnapshot;
|
||||
|
||||
/// 套餐状态
|
||||
private Status status;
|
||||
/// 订单投影状态
|
||||
@Transient
|
||||
private GaoSalesOrder.Status status;
|
||||
|
||||
/// 取消时间
|
||||
/// 订单投影取消时间
|
||||
@Transient
|
||||
private Long cancelledAt;
|
||||
|
||||
/// 取消操作人
|
||||
/// 订单投影取消操作人 ID
|
||||
@Transient
|
||||
private String cancelOperatorUserId;
|
||||
|
||||
/// 取消原因
|
||||
/// 订单投影取消原因
|
||||
@Transient
|
||||
private String cancelReason;
|
||||
|
||||
/// 备注
|
||||
@@ -70,9 +61,13 @@ public class GaoCustomerPackage extends UUIDEntity {
|
||||
@Transient
|
||||
private User cancelOperator;
|
||||
|
||||
/// 产品权益列表
|
||||
/// 关联销售订单
|
||||
@Transient
|
||||
private List<GaoCustomerPackageItem> itemList;
|
||||
private GaoSalesOrder salesOrder;
|
||||
|
||||
/// 关联订单可履约明细投影
|
||||
@Transient
|
||||
private List<GaoSalesOrderItem> itemList;
|
||||
|
||||
/// 统一履约历史
|
||||
@Transient
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 客户套餐产品权益快照
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoCustomerPackageItem extends UUIDEntity {
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 产品 ID
|
||||
private String productId;
|
||||
|
||||
/// 产品单位 ID
|
||||
private String productUnitId;
|
||||
|
||||
/// 产品图片附件
|
||||
@Transient
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 产品名称快照
|
||||
private String productNameSnapshot;
|
||||
|
||||
/// 产品单位快照
|
||||
private String unitSnapshot;
|
||||
|
||||
/// 基础单位数量系数快照
|
||||
private BigDecimal baseQuantityPerUnitSnapshot;
|
||||
|
||||
/// 总权益数量
|
||||
private BigDecimal totalQuantity;
|
||||
|
||||
/// 待核销占用数量
|
||||
private BigDecimal reservedQuantity;
|
||||
|
||||
/// 已履约数量
|
||||
private BigDecimal fulfilledQuantity;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 待核销数量
|
||||
@Transient
|
||||
private BigDecimal pendingQuantity;
|
||||
|
||||
/// 当前可发数量
|
||||
@Transient
|
||||
private BigDecimal availableQuantity;
|
||||
|
||||
/// 权益剩余数量
|
||||
@Transient
|
||||
private BigDecimal remainingQuantity;
|
||||
}
|
||||
@@ -7,10 +7,8 @@ import com.imyeyu.spring.annotation.table.Id;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.Creatable;
|
||||
import com.imyeyu.spring.entity.IDEntity;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import com.imyeyu.spring.entity.Updatable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -21,16 +19,6 @@ import java.util.List;
|
||||
@Data
|
||||
public class GaoOrderFulfillment implements IDEntity<String>, Creatable, Updatable {
|
||||
|
||||
/// 履约来源类型
|
||||
public enum FulfillmentType {
|
||||
|
||||
/// 客户套餐权益
|
||||
PACKAGE_ENTITLEMENT,
|
||||
|
||||
/// 直接产品订单项
|
||||
DIRECT_PRODUCT
|
||||
}
|
||||
|
||||
/// 履约状态
|
||||
public enum Status {
|
||||
|
||||
@@ -66,9 +54,6 @@ public class GaoOrderFulfillment implements IDEntity<String>, Creatable, Updatab
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 履约来源类型
|
||||
private FulfillmentType fulfillmentType;
|
||||
|
||||
/// 履约状态
|
||||
private Status status;
|
||||
|
||||
|
||||
@@ -35,12 +35,9 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
|
||||
@Transient
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 直接订单明细 ID
|
||||
/// 来源销售订单明细 ID
|
||||
private String sourceOrderItemId;
|
||||
|
||||
/// 客户套餐权益项 ID
|
||||
private String sourceCustomerPackageItemId;
|
||||
|
||||
/// 本次履约数量
|
||||
private BigDecimal quantity;
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class GaoSalesOrder extends UUIDEntity {
|
||||
@Transient
|
||||
private User cancelOperator;
|
||||
|
||||
/// 直接产品订单明细
|
||||
/// 销售订单明细
|
||||
@Transient
|
||||
private List<GaoSalesOrderItem> itemList;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 直接产品订单明细
|
||||
/// GAO 销售订单通用可履约明细
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-09-02
|
||||
@@ -17,9 +17,22 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoSalesOrderItem extends UUIDEntity {
|
||||
|
||||
/// 订单项类型
|
||||
public enum ItemType {
|
||||
|
||||
/// 直接产品
|
||||
DIRECT_PRODUCT,
|
||||
|
||||
/// 套餐权益
|
||||
PACKAGE_ENTITLEMENT
|
||||
}
|
||||
|
||||
/// 销售订单 ID
|
||||
private String salesOrderId;
|
||||
|
||||
/// 订单项类型
|
||||
private ItemType itemType;
|
||||
|
||||
/// 产品 ID
|
||||
private String productId;
|
||||
|
||||
@@ -59,4 +72,16 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 待核销数量
|
||||
@Transient
|
||||
private BigDecimal pendingQuantity;
|
||||
|
||||
/// 当前可履约数量
|
||||
@Transient
|
||||
private BigDecimal availableQuantity;
|
||||
|
||||
/// 权益剩余数量
|
||||
@Transient
|
||||
private BigDecimal remainingQuantity;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 客户套餐产品权益 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoCustomerPackageItemMapper extends BaseMapper<GaoCustomerPackageItem, String> {
|
||||
|
||||
/// 按客户套餐查询产品权益
|
||||
List<GaoCustomerPackageItem> selectByCustomerPackageId(@Param("customerPackageId") String customerPackageId);
|
||||
|
||||
/// 批量查询客户套餐产品权益
|
||||
List<GaoCustomerPackageItem> selectByCustomerPackageIdList(@Param("customerPackageIdList") Collection<String> customerPackageIdList);
|
||||
|
||||
/// 锁定客户套餐产品
|
||||
GaoCustomerPackageItem selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 调整待核销占用数量
|
||||
int updateReservedQuantity(@Param("id") String id, @Param("delta") java.math.BigDecimal delta);
|
||||
|
||||
/// 调整已履约数量
|
||||
int updateFulfilledQuantity(@Param("id") String id, @Param("delta") java.math.BigDecimal delta);
|
||||
}
|
||||
@@ -11,9 +11,6 @@ import org.apache.ibatis.annotations.Param;
|
||||
/// @since 2026-08-31
|
||||
public interface GaoCustomerPackageMapper extends BaseMapper<GaoCustomerPackage, String> {
|
||||
|
||||
/// 锁定客户套餐
|
||||
GaoCustomerPackage selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 按销售订单查询客户套餐
|
||||
GaoCustomerPackage selectBySalesOrderId(@Param("salesOrderId") String salesOrderId);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
/// GAO 销售订单 Mapper
|
||||
///
|
||||
@@ -19,6 +20,9 @@ public interface GaoSalesOrderMapper extends BaseMapper<GaoSalesOrder, String> {
|
||||
/// 锁定订单
|
||||
GaoSalesOrder selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 批量查询订单
|
||||
List<GaoSalesOrder> selectByIdList(@Param("idList") Collection<String> idList);
|
||||
|
||||
/// 按条件统计订单
|
||||
long countByQuery(GaoSalesOrderPage page);
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 客户套餐服务
|
||||
///
|
||||
@@ -22,55 +17,12 @@ public interface GaoCustomerPackageService extends BaseService<GaoCustomerPackag
|
||||
/// 按条件查询客户套餐
|
||||
PageResult<GaoCustomerPackage> pageByQuery(GaoCustomerPackagePage page);
|
||||
|
||||
/// 创建客户套餐及购买事件登记
|
||||
/// 创建客户套餐及套餐订单
|
||||
GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request);
|
||||
|
||||
/// 取消客户套餐
|
||||
GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request);
|
||||
|
||||
/// 锁定客户套餐
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
/// @return 已锁定的客户套餐,不存在时为空
|
||||
GaoCustomerPackage getForUpdate(String id);
|
||||
|
||||
/// 按销售订单查询客户套餐
|
||||
GaoCustomerPackage getBySalesOrderId(String salesOrderId);
|
||||
|
||||
/// 查询客户套餐产品权益
|
||||
///
|
||||
/// @param customerPackageId 客户套餐 ID
|
||||
/// @return 产品权益列表
|
||||
List<GaoCustomerPackageItem> listItemByCustomerPackageId(String customerPackageId);
|
||||
|
||||
/// 批量查询客户套餐产品权益
|
||||
///
|
||||
/// @param customerPackageIdList 客户套餐 ID 列表
|
||||
/// @return 按客户套餐 ID 分组的产品权益
|
||||
Map<String, List<GaoCustomerPackageItem>> mapItemListByCustomerPackageIdList(Collection<String> customerPackageIdList);
|
||||
|
||||
/// 锁定客户套餐产品权益
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @return 已锁定的产品权益,不存在时为空
|
||||
GaoCustomerPackageItem getItemForUpdate(String itemId);
|
||||
|
||||
/// 原子调整待核销发放占用数量
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updateItemReservedQuantity(String itemId, BigDecimal delta);
|
||||
|
||||
/// 原子调整已履约数量
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updateItemFulfilledQuantity(String itemId, BigDecimal delta);
|
||||
|
||||
/// 保存客户套餐聚合状态
|
||||
///
|
||||
/// @param packageEntity 客户套餐聚合
|
||||
void updateAggregateState(GaoCustomerPackage packageEntity);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 销售订单服务
|
||||
///
|
||||
@@ -68,9 +70,12 @@ public interface GaoSalesOrderService extends BaseService<GaoSalesOrder, String>
|
||||
/// 根据订单明细刷新订单履约状态
|
||||
void refreshFulfillmentStatus(GaoSalesOrder order);
|
||||
|
||||
/// 写入套餐订单履约状态
|
||||
/// 写入销售订单履约状态
|
||||
void updateFulfillmentStatus(GaoSalesOrder order, boolean fulfilled);
|
||||
|
||||
/// 查询订单明细
|
||||
List<GaoSalesOrderItem> listItemByOrderId(String orderId);
|
||||
|
||||
/// 批量查询订单及其明细
|
||||
Map<String, GaoSalesOrder> mapByIdList(Collection<String> idList);
|
||||
}
|
||||
|
||||
+34
-70
@@ -6,11 +6,10 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageItemMapper;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
@@ -39,7 +38,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -55,10 +53,7 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
public class GaoCustomerPackageServiceImplement extends AbstractEntityService<GaoCustomerPackage, String> implements GaoCustomerPackageService {
|
||||
|
||||
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
||||
|
||||
private final GaoCustomerPackageMapper mapper;
|
||||
private final GaoCustomerPackageItemMapper itemMapper;
|
||||
private final GaoPackageConfigService packageConfigService;
|
||||
private final GaoProductService productService;
|
||||
private final GaoCustomerService customerService;
|
||||
@@ -74,42 +69,12 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackage getForUpdate(String id) {
|
||||
return mapper.selectForUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackage getBySalesOrderId(String salesOrderId) {
|
||||
TimiException.required(salesOrderId, "not found salesOrderId");
|
||||
return mapper.selectBySalesOrderId(salesOrderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoCustomerPackageItem> listItemByCustomerPackageId(String customerPackageId) {
|
||||
return itemMapper.selectByCustomerPackageId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackageItem getItemForUpdate(String itemId) {
|
||||
return itemMapper.selectForUpdate(itemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateItemReservedQuantity(String itemId, BigDecimal delta) {
|
||||
return itemMapper.updateReservedQuantity(itemId, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateItemFulfilledQuantity(String itemId, BigDecimal delta) {
|
||||
return itemMapper.updateFulfilledQuantity(itemId, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAggregateState(GaoCustomerPackage packageEntity) {
|
||||
mapper.update(packageEntity);
|
||||
}
|
||||
|
||||
/// 客户套餐必须通过购买流程创建
|
||||
///
|
||||
/// @param packageEntity 客户套餐
|
||||
@@ -204,6 +169,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
verifySameQuantityAsConfig(itemList, configItemList);
|
||||
}
|
||||
|
||||
List<GaoSalesOrderItem> orderItemList = itemList.stream().map(this::toOrderItem).toList();
|
||||
GaoPackageOrderCreateData orderData = new GaoPackageOrderCreateData();
|
||||
orderData.setStoreId(config.getStoreId());
|
||||
orderData.setCustomerId(customer.getId());
|
||||
@@ -215,6 +181,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
orderData.setPurchasedAt(purchasedAt);
|
||||
orderData.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
orderData.setRemark(request.getRemark());
|
||||
orderData.setItemList(orderItemList);
|
||||
GaoSalesOrder salesOrder = salesOrderService.createPackageOrder(orderData);
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, salesOrder.getId(), request.getAttachmentList());
|
||||
|
||||
@@ -225,24 +192,11 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
result.setPackageConfigId(config.getId());
|
||||
result.setPackageNameSnapshot(config.getName());
|
||||
result.setStandardAmountSnapshot(config.getStandardAmount());
|
||||
result.setStatus(GaoCustomerPackage.Status.ACTIVE);
|
||||
result.setRemark(request.getRemark());
|
||||
super.create(result);
|
||||
for (GaoCustomerPackagePurchaseItem source : itemList) {
|
||||
GaoCustomerPackageItem item = new GaoCustomerPackageItem();
|
||||
item.setCustomerPackageId(result.getId());
|
||||
item.setProductId(source.getProductId());
|
||||
item.setProductUnitId(source.getProductUnitId());
|
||||
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||
item.setUnitSnapshot(source.getUnitSnapshot());
|
||||
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||
item.setTotalQuantity(source.getTotalQuantity());
|
||||
item.setReservedQuantity(ZERO);
|
||||
item.setFulfilledQuantity(ZERO);
|
||||
item.setSort(source.getSort());
|
||||
item.setRemark(source.getRemark());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
result.setSalesOrder(salesOrder);
|
||||
result.setItemList(salesOrder.getItemList());
|
||||
result.setStatus(salesOrder.getStatus());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -256,20 +210,20 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
TimiException.required(request, "not found cancel request");
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
String reason = requiredReason(request.getReason(), "取消原因不能为空");
|
||||
GaoCustomerPackage packageEntity = mapper.selectForUpdate(request.getCustomerPackageId());
|
||||
GaoCustomerPackage packageEntity = get(request.getCustomerPackageId());
|
||||
TimiException.required(packageEntity, "not found customer package");
|
||||
storeScopeService.checkStoreId(packageEntity.getStoreId());
|
||||
TimiException.requiredTrue(packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE, "当前套餐不可取消");
|
||||
TimiException.required(packageEntity.getSalesOrderId(), "套餐未关联销售订单");
|
||||
GaoSalesOrderCancelRequest orderCancelRequest = new GaoSalesOrderCancelRequest();
|
||||
orderCancelRequest.setSalesOrderId(packageEntity.getSalesOrderId());
|
||||
orderCancelRequest.setReason(reason);
|
||||
GaoSalesOrder order = salesOrderService.cancel(orderCancelRequest);
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.CANCELLED);
|
||||
packageEntity.setSalesOrder(order);
|
||||
packageEntity.setStatus(order.getStatus());
|
||||
packageEntity.setCancelledAt(order.getCancelledAt());
|
||||
packageEntity.setCancelOperatorUserId(order.getCancelOperatorUserId());
|
||||
packageEntity.setCancelReason(reason);
|
||||
mapper.update(packageEntity);
|
||||
packageEntity.setCancelReason(order.getCancelReason());
|
||||
packageEntity.setItemList(salesOrderService.listItemByOrderId(order.getId()));
|
||||
return packageEntity;
|
||||
}
|
||||
|
||||
@@ -295,6 +249,24 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private GaoSalesOrderItem toOrderItem(GaoCustomerPackagePurchaseItem source) {
|
||||
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
||||
item.setItemType(GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT);
|
||||
item.setProductId(source.getProductId());
|
||||
item.setProductUnitId(source.getProductUnitId());
|
||||
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||
item.setUnitSnapshot(source.getUnitSnapshot());
|
||||
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||
item.setQuantity(source.getTotalQuantity());
|
||||
item.setReservedQuantity(BigDecimal.ZERO);
|
||||
item.setFulfilledQuantity(BigDecimal.ZERO);
|
||||
item.setUnitPrice(null);
|
||||
item.setLineAmount(null);
|
||||
item.setSort(source.getSort());
|
||||
item.setRemark(source.getRemark());
|
||||
return item;
|
||||
}
|
||||
|
||||
private void verifyPurchaseItems(List<GaoCustomerPackagePurchaseItem> itemList) {
|
||||
TimiException.requiredTrue(!itemList.isEmpty(), "购买套餐至少需要一个产品");
|
||||
Set<String> keySet = new HashSet<>();
|
||||
@@ -351,29 +323,21 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
||||
TimiException.requiredTrue(existingPackage.getPackageConfigId().equals(request.getPackageConfigId())
|
||||
&& (request.getPurchasedAt() == null || existingOrder.getPurchasedAt().equals(request.getPurchasedAt()))
|
||||
&& sameText(existingOrder.getRemark(), request.getRemark()), "requestNo 对应购买请求内容不一致");
|
||||
List<GaoCustomerPackageItem> existingItemList = itemMapper.selectByCustomerPackageId(existingPackage.getId());
|
||||
List<GaoSalesOrderItem> existingItemList = salesOrderService.listItemByOrderId(existingOrder.getId());
|
||||
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
||||
verifyPurchaseItems(requestItemList);
|
||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
||||
Map<String, GaoCustomerPackageItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), Function.identity()));
|
||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), Function.identity()));
|
||||
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
||||
GaoCustomerPackageItem existingItem = existingItemMap.get(item.getProductId() + "|" + item.getProductUnitId());
|
||||
GaoSalesOrderItem existingItem = existingItemMap.get(item.getProductId() + "|" + item.getProductUnitId());
|
||||
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
||||
TimiException.requiredTrue(existingItem.getTotalQuantity().compareTo(item.getTotalQuantity()) == 0
|
||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||
&& existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0
|
||||
&& existingItem.getSort().equals(item.getSort())
|
||||
&& sameText(existingItem.getRemark(), item.getRemark()), "requestNo 对应购买请求内容不一致");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<GaoCustomerPackageItem>> mapItemListByCustomerPackageIdList(Collection<String> customerPackageIdList) {
|
||||
if (customerPackageIdList == null || customerPackageIdList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return itemMapper.selectByCustomerPackageIdList(customerPackageIdList).stream()
|
||||
.collect(Collectors.groupingBy(GaoCustomerPackageItem::getCustomerPackageId));
|
||||
}
|
||||
|
||||
private String requiredReason(String reason, String message) {
|
||||
TimiException.required(reason, message);
|
||||
String result = reason.trim();
|
||||
|
||||
+80
-155
@@ -5,8 +5,6 @@ import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||
@@ -16,7 +14,6 @@ import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillment;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillmentItem;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoOrderFulfillmentItemMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoOrderFulfillmentMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
||||
@@ -70,7 +67,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
private final GaoOrderFulfillmentMapper mapper;
|
||||
private final GaoOrderFulfillmentItemMapper itemMapper;
|
||||
private final GaoSalesOrderService salesOrderService;
|
||||
private final GaoCustomerPackageService customerPackageService;
|
||||
private final GaoProductService productService;
|
||||
private final GaoProductUnitService unitService;
|
||||
private final GaoProductStockService productStockService;
|
||||
@@ -166,13 +162,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "履约必须上传照片");
|
||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能履约");
|
||||
salesOrderService.checkFulfillmentPermission(order);
|
||||
GaoCustomerPackage packageEntity = lockPackage(order, true);
|
||||
List<SourceEntry> sourceList = loadSources(order, packageEntity, requestItems(request.getItemList()), true);
|
||||
List<SourceEntry> sourceList = loadSources(order, request.getItemList(), true);
|
||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||
boolean stockReady = stockItems.isEmpty() || productStockService.hasSufficientForFulfillment(order.getStoreId(), stockItems);
|
||||
String operatorUserId = userLoginService.getRequireLoginUserId();
|
||||
GaoOrderFulfillment fulfillment = newFulfillment(order, sourceList.getFirst().packageSource()
|
||||
? GaoOrderFulfillment.FulfillmentType.PACKAGE_ENTITLEMENT : GaoOrderFulfillment.FulfillmentType.DIRECT_PRODUCT,
|
||||
GaoOrderFulfillment fulfillment = newFulfillment(order,
|
||||
stockReady ? GaoOrderFulfillment.Status.PENDING : GaoOrderFulfillment.Status.WAITING_STOCK,
|
||||
requestNo, operatorUserId, TimiJava.defaultIfNull(request.getDeliveredAt(), Time.now()), request.getRemark());
|
||||
super.create(fulfillment);
|
||||
@@ -195,15 +189,17 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
TimiException.required(request, "not found prepare stock request");
|
||||
TimiException.required(request.getFulfillmentId(), "not found fulfillmentId");
|
||||
roleChecker.checkAny(ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name());
|
||||
GaoOrderFulfillment fulfillment = mapper.selectForUpdate(request.getFulfillmentId());
|
||||
TimiException.required(fulfillment, "not found fulfillment");
|
||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||
TimiException.required(snapshot, "not found fulfillment");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK, "履约单不是等待库存状态");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(fulfillment.getSalesOrderId());
|
||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能准备库存");
|
||||
salesOrderService.checkFulfillmentPermission(order);
|
||||
GaoCustomerPackage packageEntity = lockPackage(order, false);
|
||||
List<SourceEntry> sourceList = loadSources(order, packageEntity, copyRequestItems(itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId())), false);
|
||||
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
||||
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||
TimiException.requiredTrue(!stockItems.isEmpty(), "履约单没有需要占用的实物库存");
|
||||
TimiException.requiredTrue(productStockService.hasSufficientForFulfillment(order.getStoreId(), stockItems), "库存仍然不足,不能准备履约");
|
||||
@@ -226,16 +222,17 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
private GaoOrderFulfillment verifyInternal(GaoOrderFulfillmentActionRequest request) {
|
||||
TimiException.required(request, "not found verify request");
|
||||
TimiException.required(request.getFulfillmentId(), "not found fulfillmentId");
|
||||
GaoOrderFulfillment fulfillment = mapper.selectForUpdate(request.getFulfillmentId());
|
||||
TimiException.required(fulfillment, "not found fulfillment");
|
||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||
TimiException.required(snapshot, "not found fulfillment");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING, "履约单不是待核销状态");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(fulfillment.getSalesOrderId());
|
||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能核销履约");
|
||||
salesOrderService.checkFulfillmentPermission(order);
|
||||
GaoCustomerPackage packageEntity = lockPackage(order, false);
|
||||
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
||||
List<SourceEntry> sourceList = loadSources(order, packageEntity, copyRequestItems(fulfillmentItemList), false);
|
||||
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
||||
assertReserved(sourceList);
|
||||
for (SourceEntry source : sourceList) {
|
||||
decreaseReserved(source);
|
||||
@@ -249,7 +246,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
}
|
||||
TimiException.requiredTrue(1 == mapper.updateStatus(fulfillment.getId(), GaoOrderFulfillment.Status.VERIFIED,
|
||||
verifierUserId, Time.now(), request.getReason()), "履约状态已变化");
|
||||
refreshAggregateStatus(order, packageEntity);
|
||||
salesOrderService.refreshFulfillmentStatus(order);
|
||||
return detail(fulfillment.getId());
|
||||
}
|
||||
|
||||
@@ -267,7 +264,8 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
|
||||
private GaoOrderFulfillment cancelInternal(GaoOrderFulfillmentActionRequest request) {
|
||||
TimiException.required(request, "not found cancel request");
|
||||
GaoOrderFulfillment fulfillment = mapper.selectForUpdate(request.getFulfillmentId());
|
||||
TimiException.required(request.getFulfillmentId(), "not found fulfillmentId");
|
||||
GaoOrderFulfillment fulfillment = mapper.selectById(request.getFulfillmentId());
|
||||
TimiException.required(fulfillment, "not found fulfillment");
|
||||
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||
TimiException.requiredTrue(loginUserId.equals(fulfillment.getOperatorUserId()), "只能撤销自己提交的履约");
|
||||
@@ -278,14 +276,16 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
TimiException.required(request, "not found action request");
|
||||
TimiException.required(request.getFulfillmentId(), "not found fulfillmentId");
|
||||
String reason = requiredText(request.getReason(), "拒绝或撤销原因不能为空");
|
||||
GaoOrderFulfillment fulfillment = mapper.selectForUpdate(request.getFulfillmentId());
|
||||
TimiException.required(fulfillment, "not found fulfillment");
|
||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||
TimiException.required(snapshot, "not found fulfillment");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK
|
||||
|| fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING, "履约单不是可释放状态");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(fulfillment.getSalesOrderId());
|
||||
GaoCustomerPackage packageEntity = lockPackage(order, false);
|
||||
List<SourceEntry> sourceList = loadSources(order, packageEntity, copyRequestItems(itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId())), false);
|
||||
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
||||
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
||||
if (fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING) {
|
||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||
if (!stockItems.isEmpty()) {
|
||||
@@ -312,37 +312,43 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
String requestNo = requiredText(request.getRequestNo(), "not found requestNo");
|
||||
TimiException.required(request.getFulfillmentId(), "not found fulfillmentId");
|
||||
String reason = requiredText(request.getReason(), "冲正原因不能为空");
|
||||
GaoOrderFulfillment source = mapper.selectForUpdate(request.getFulfillmentId());
|
||||
TimiException.required(source, "not found fulfillment");
|
||||
storeScopeService.checkStoreId(source.getStoreId());
|
||||
GaoOrderFulfillment existing = mapper.selectByRequestNo(source.getStoreId(), requestNo);
|
||||
GaoOrderFulfillment sourceSnapshot = mapper.selectById(request.getFulfillmentId());
|
||||
TimiException.required(sourceSnapshot, "not found fulfillment");
|
||||
GaoOrderFulfillment existing = mapper.selectByRequestNo(sourceSnapshot.getStoreId(), requestNo);
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(source.getId().equals(existing.getSourceFulfillmentId()), "requestNo 已被其他履约操作使用");
|
||||
TimiException.requiredTrue(sourceSnapshot.getId().equals(existing.getSourceFulfillmentId()), "requestNo 已被其他履约操作使用");
|
||||
return detail(existing.getId());
|
||||
}
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(sourceSnapshot.getSalesOrderId());
|
||||
List<GaoOrderFulfillmentItem> sourceItemList = itemMapper.selectByFulfillmentId(sourceSnapshot.getId());
|
||||
List<SourceEntry> sourceList = loadSources(order, copyRequestItems(sourceItemList), false);
|
||||
GaoOrderFulfillment source = lockFulfillment(sourceSnapshot.getId(), order);
|
||||
storeScopeService.checkStoreId(source.getStoreId());
|
||||
TimiException.requiredTrue(source.getStatus() == GaoOrderFulfillment.Status.VERIFIED && source.getSourceFulfillmentId() == null, "只有原始已核销履约可以冲正");
|
||||
TimiException.requiredNull(mapper.selectBySourceFulfillmentId(source.getId()), "该履约单已经冲正");
|
||||
GaoSalesOrder order = salesOrderService.getForUpdate(source.getSalesOrderId());
|
||||
GaoCustomerPackage packageEntity = lockPackage(order, false);
|
||||
List<GaoOrderFulfillmentItem> sourceItemList = itemMapper.selectByFulfillmentIdForUpdate(source.getId());
|
||||
List<SourceEntry> sourceList = loadSources(order, packageEntity, copyRequestItems(sourceItemList), false);
|
||||
sourceItemList = itemMapper.selectByFulfillmentIdForUpdate(source.getId());
|
||||
TimiException.requiredTrue(!sourceItemList.isEmpty(), "履约单没有明细");
|
||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||
if (!stockItems.isEmpty()) {
|
||||
TimiException.requiredTrue(Boolean.TRUE.equals(request.getReturnedToStore()), "实物履约冲正必须确认已退回门店");
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "实物履约冲正必须上传退回照片");
|
||||
}
|
||||
String operatorUserId = userLoginService.getRequireLoginUserId();
|
||||
GaoOrderFulfillment reverse = newFulfillment(order, source.getFulfillmentType(), GaoOrderFulfillment.Status.VERIFIED,
|
||||
GaoOrderFulfillment reverse = newFulfillment(order, GaoOrderFulfillment.Status.VERIFIED,
|
||||
requestNo, operatorUserId, TimiJava.defaultIfNull(request.getDeliveredAt(), Time.now()), request.getRemark());
|
||||
reverse.setVerifierUserId(operatorUserId);
|
||||
reverse.setVerifiedAt(Time.now());
|
||||
reverse.setSourceFulfillmentId(source.getId());
|
||||
reverse.setReason(reason);
|
||||
super.create(reverse);
|
||||
for (int i = 0; i < sourceList.size(); i++) {
|
||||
SourceEntry sourceEntry = sourceList.get(i);
|
||||
Map<String, GaoOrderFulfillmentItem> sourceItemMap = new HashMap<>();
|
||||
for (GaoOrderFulfillmentItem sourceItem : sourceItemList) {
|
||||
sourceItemMap.put(sourceItem.getSourceOrderItemId(), sourceItem);
|
||||
}
|
||||
for (SourceEntry sourceEntry : sourceList) {
|
||||
decreaseFulfilled(sourceEntry);
|
||||
GaoOrderFulfillmentItem sourceItem = sourceItemList.get(i);
|
||||
GaoOrderFulfillmentItem sourceItem = sourceItemMap.get(sourceEntry.sourceId());
|
||||
TimiException.required(sourceItem, "履约明细来源订单项不存在");
|
||||
GaoOrderFulfillmentItem reverseItem = copyFulfillmentItem(sourceItem);
|
||||
reverseItem.setFulfillmentId(reverse.getId());
|
||||
reverseItem.setReservedBefore(sourceEntry.reservedBefore());
|
||||
@@ -365,17 +371,16 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
TimiException.requiredTrue(1 == mapper.updateStatus(source.getId(), GaoOrderFulfillment.Status.REVOKED,
|
||||
operatorUserId, Time.now(), reason), "原履约状态已变化");
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_ORDER_FULFILLMENT, reverse.getId(), request.getAttachmentList());
|
||||
refreshAggregateStatus(order, packageEntity);
|
||||
salesOrderService.refreshFulfillmentStatus(order);
|
||||
return detail(reverse.getId());
|
||||
}
|
||||
|
||||
private GaoOrderFulfillment newFulfillment(GaoSalesOrder order, GaoOrderFulfillment.FulfillmentType type,
|
||||
GaoOrderFulfillment.Status status, String requestNo, String operatorUserId, Long deliveredAt, String remark) {
|
||||
private GaoOrderFulfillment newFulfillment(GaoSalesOrder order, GaoOrderFulfillment.Status status,
|
||||
String requestNo, String operatorUserId, Long deliveredAt, String remark) {
|
||||
GaoOrderFulfillment result = new GaoOrderFulfillment();
|
||||
result.setSalesOrderId(order.getId());
|
||||
result.setStoreId(order.getStoreId());
|
||||
result.setCustomerId(order.getCustomerId());
|
||||
result.setFulfillmentType(type);
|
||||
result.setStatus(status);
|
||||
result.setRequestNo(requestNo);
|
||||
result.setOperatorUserId(operatorUserId);
|
||||
@@ -386,40 +391,24 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
|
||||
private void insertSourceReservations(GaoOrderFulfillment fulfillment, List<SourceEntry> sourceList) {
|
||||
for (SourceEntry source : sourceList) {
|
||||
if (source.packageSource()) {
|
||||
TimiException.requiredTrue(1 == customerPackageService.updateItemReservedQuantity(source.sourceId(), source.quantity()), "套餐权益占用失败");
|
||||
} else {
|
||||
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedQuantity(source.sourceId(), source.quantity()), "订单明细占用失败");
|
||||
}
|
||||
GaoOrderFulfillmentItem item = source.toFulfillmentItem(fulfillment.getId());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void decreaseReserved(SourceEntry source) {
|
||||
if (source.packageSource()) {
|
||||
TimiException.requiredTrue(1 == customerPackageService.updateItemReservedQuantity(source.sourceId(), source.quantity().negate()), "套餐权益占用释放失败");
|
||||
} else {
|
||||
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedQuantity(source.sourceId(), source.quantity().negate()), "订单明细占用释放失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void increaseFulfilled(SourceEntry source) {
|
||||
if (source.packageSource()) {
|
||||
TimiException.requiredTrue(1 == customerPackageService.updateItemFulfilledQuantity(source.sourceId(), source.quantity()), "套餐权益履约失败");
|
||||
} else {
|
||||
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledQuantity(source.sourceId(), source.quantity()), "订单明细履约失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void decreaseFulfilled(SourceEntry source) {
|
||||
TimiException.requiredTrue(0 <= source.fulfilledBefore().compareTo(source.quantity()), "已履约数量不足以冲正");
|
||||
if (source.packageSource()) {
|
||||
TimiException.requiredTrue(1 == customerPackageService.updateItemFulfilledQuantity(source.sourceId(), source.quantity().negate()), "套餐权益冲正失败");
|
||||
} else {
|
||||
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledQuantity(source.sourceId(), source.quantity().negate()), "订单明细冲正失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertReserved(List<SourceEntry> sourceList) {
|
||||
for (SourceEntry source : sourceList) {
|
||||
@@ -427,67 +416,48 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
}
|
||||
}
|
||||
|
||||
private List<SourceEntry> loadSources(GaoSalesOrder order, GaoCustomerPackage packageEntity,
|
||||
List<GaoOrderFulfillmentItemRequest> requestItems, boolean checkAvailable) {
|
||||
private List<SourceEntry> loadSources(GaoSalesOrder order, List<GaoOrderFulfillmentItemRequest> requestItems, boolean checkAvailable) {
|
||||
TimiException.requiredTrue(requestItems != null && !requestItems.isEmpty(), "履约至少需要一个产品");
|
||||
List<GaoOrderFulfillmentItemRequest> sortedRequestItems = new ArrayList<>(requestItems);
|
||||
Set<String> sourceIdSet = new HashSet<>();
|
||||
List<SourceEntry> result = new ArrayList<>();
|
||||
for (GaoOrderFulfillmentItemRequest requestItem : requestItems) {
|
||||
for (GaoOrderFulfillmentItemRequest requestItem : sortedRequestItems) {
|
||||
TimiException.required(requestItem, "履约明细不能为空");
|
||||
boolean hasOrderSource = hasText(requestItem.getSourceOrderItemId());
|
||||
boolean hasPackageSource = hasText(requestItem.getSourceCustomerPackageItemId());
|
||||
TimiException.requiredTrue(hasOrderSource ^ hasPackageSource, "履约明细来源必须二选一");
|
||||
String sourceId = hasOrderSource ? requestItem.getSourceOrderItemId() : requestItem.getSourceCustomerPackageItemId();
|
||||
String sourceId = requiredText(requestItem.getSourceOrderItemId(), "履约明细来源订单项不能为空");
|
||||
requestItem.setSourceOrderItemId(sourceId);
|
||||
TimiException.requiredTrue(sourceIdSet.add(sourceId), "同一履约单不能重复来源明细");
|
||||
TimiException.required(requestItem.getQuantity(), "履约数量不能为空");
|
||||
TimiException.requiredTrue(0 < requestItem.getQuantity().signum(), "履约数量必须大于 0");
|
||||
TimiException.requiredTrue(3 >= requestItem.getQuantity().stripTrailingZeros().scale(), "履约数量最多保留三位小数");
|
||||
if (order.getOrderType() == GaoSalesOrder.OrderType.DIRECT_PRODUCT) {
|
||||
TimiException.requiredTrue(hasOrderSource && !hasPackageSource, "直接产品订单只能引用订单明细");
|
||||
GaoSalesOrderItem source = salesOrderService.getItemForUpdate(sourceId);
|
||||
}
|
||||
sortedRequestItems.sort(Comparator.comparing(GaoOrderFulfillmentItemRequest::getSourceOrderItemId));
|
||||
List<SourceEntry> result = new ArrayList<>();
|
||||
for (GaoOrderFulfillmentItemRequest requestItem : sortedRequestItems) {
|
||||
GaoSalesOrderItem source = salesOrderService.getItemForUpdate(requestItem.getSourceOrderItemId());
|
||||
TimiException.required(source, "not found sales order item");
|
||||
TimiException.requiredTrue(order.getId().equals(source.getSalesOrderId()), "订单明细不属于当前订单");
|
||||
result.add(toSource(order, source, requestItem.getQuantity(), false, checkAvailable));
|
||||
} else {
|
||||
TimiException.requiredTrue(hasPackageSource && !hasOrderSource, "套餐订单只能引用客户套餐权益");
|
||||
TimiException.required(packageEntity, "订单未找到客户套餐权益");
|
||||
GaoCustomerPackageItem source = customerPackageService.getItemForUpdate(sourceId);
|
||||
TimiException.required(source, "not found customer package item");
|
||||
TimiException.requiredTrue(packageEntity.getId().equals(source.getCustomerPackageId()), "套餐权益不属于当前订单");
|
||||
result.add(toSource(order, source, requestItem.getQuantity(), true, checkAvailable));
|
||||
}
|
||||
GaoSalesOrderItem.ItemType expectedItemType = order.getOrderType() == GaoSalesOrder.OrderType.PACKAGE
|
||||
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
||||
TimiException.requiredTrue(source.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||
result.add(toSource(order, source, requestItem.getQuantity(), checkAvailable));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, BigDecimal quantity, boolean packageSource, boolean checkAvailable) {
|
||||
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, BigDecimal quantity, boolean checkAvailable) {
|
||||
GaoProduct product = productService.get(source.getProductId());
|
||||
TimiException.required(product, "履约来源产品不存在");
|
||||
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
||||
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
|
||||
GaoProductUnit unit = snapshotUnit(source.getProductUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
|
||||
return buildSource(order, source.getId(), null, source.getProductId(), source.getProductUnitId(), source.getProductNameSnapshot(),
|
||||
return buildSource(order, source.getId(), source.getProductId(), source.getProductUnitId(), source.getProductNameSnapshot(),
|
||||
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getQuantity(), source.getReservedQuantity(), source.getFulfilledQuantity(),
|
||||
product, unit, packageSource, checkAvailable);
|
||||
product, unit, checkAvailable);
|
||||
}
|
||||
|
||||
private SourceEntry toSource(GaoSalesOrder order, GaoCustomerPackageItem source, BigDecimal quantity, boolean packageSource, boolean checkAvailable) {
|
||||
TimiException.required(source.getProductId(), "套餐权益缺少产品快照,不能履约");
|
||||
TimiException.required(source.getProductUnitId(), "套餐权益缺少单位快照,不能履约");
|
||||
GaoProduct product = productService.get(source.getProductId());
|
||||
TimiException.required(product, "履约来源产品不存在");
|
||||
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
||||
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
|
||||
GaoProductUnit unit = snapshotUnit(source.getProductUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
|
||||
return buildSource(order, null, source.getId(), source.getProductId(), source.getProductUnitId(), source.getProductNameSnapshot(),
|
||||
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getTotalQuantity(), source.getReservedQuantity(), fulfilledQuantity(source),
|
||||
product, unit, packageSource, checkAvailable);
|
||||
}
|
||||
|
||||
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String packageItemId, String productId, String unitId,
|
||||
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String productId, String unitId,
|
||||
String productName, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
|
||||
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductUnit unit,
|
||||
boolean packageSource, boolean checkAvailable) {
|
||||
boolean checkAvailable) {
|
||||
if (!Boolean.TRUE.equals(product.getAllowDecimalQuantity())) {
|
||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
||||
}
|
||||
@@ -497,8 +467,8 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
BigDecimal available = decimal(totalQuantity).subtract(reservedBefore).subtract(fulfilledBefore);
|
||||
TimiException.requiredTrue(0 <= available.compareTo(quantity), "来源产品可履约数量不足");
|
||||
}
|
||||
return new SourceEntry(orderItemId, packageItemId, productId, unitId, productName, unitName, baseQuantityPerUnit,
|
||||
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, unit, packageSource);
|
||||
return new SourceEntry(orderItemId, productId, unitId, productName, unitName, baseQuantityPerUnit,
|
||||
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, unit);
|
||||
}
|
||||
|
||||
private List<GaoStockChangeItem> buildStockItems(String storeId, List<SourceEntry> sourceList) {
|
||||
@@ -523,51 +493,15 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
return itemMap.values().stream().sorted(Comparator.comparing(GaoStockChangeItem::getProductId)).toList();
|
||||
}
|
||||
|
||||
private GaoCustomerPackage lockPackage(GaoSalesOrder order, boolean requireActive) {
|
||||
if (order.getOrderType() != GaoSalesOrder.OrderType.PACKAGE) {
|
||||
return null;
|
||||
}
|
||||
GaoCustomerPackage source = customerPackageService.getBySalesOrderId(order.getId());
|
||||
TimiException.required(source, "订单未找到客户套餐权益");
|
||||
GaoCustomerPackage result = customerPackageService.getForUpdate(source.getId());
|
||||
TimiException.required(result, "not found customer package");
|
||||
if (requireActive) {
|
||||
TimiException.requiredTrue(result.getStatus() == GaoCustomerPackage.Status.ACTIVE, "当前套餐不可履约");
|
||||
} else {
|
||||
TimiException.requiredTrue(result.getStatus() != GaoCustomerPackage.Status.CANCELLED, "已取消套餐不能履约");
|
||||
}
|
||||
private GaoOrderFulfillment lockFulfillment(String fulfillmentId, GaoSalesOrder order) {
|
||||
GaoOrderFulfillment result = mapper.selectForUpdate(fulfillmentId);
|
||||
TimiException.required(result, "not found fulfillment");
|
||||
TimiException.requiredTrue(order.getId().equals(result.getSalesOrderId()), "履约单不属于当前订单");
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal fulfilledQuantity(GaoCustomerPackageItem item) {
|
||||
return item.getFulfilledQuantity();
|
||||
}
|
||||
|
||||
private void refreshAggregateStatus(GaoSalesOrder order, GaoCustomerPackage packageEntity) {
|
||||
if (packageEntity != null) {
|
||||
List<GaoCustomerPackageItem> itemList = customerPackageService.listItemByCustomerPackageId(packageEntity.getId());
|
||||
boolean fulfilled = !itemList.isEmpty();
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
if (decimal(fulfilledQuantity(item)).compareTo(decimal(item.getTotalQuantity())) < 0) {
|
||||
fulfilled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fulfilled && packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE) {
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.FULFILLED);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
} else if (!fulfilled && packageEntity.getStatus() == GaoCustomerPackage.Status.FULFILLED) {
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.ACTIVE);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
salesOrderService.updateFulfillmentStatus(order, fulfilled);
|
||||
} else {
|
||||
salesOrderService.refreshFulfillmentStatus(order);
|
||||
}
|
||||
}
|
||||
|
||||
private List<GaoOrderFulfillmentItemRequest> requestItems(List<GaoOrderFulfillmentItemRequest> itemList) {
|
||||
return itemList;
|
||||
private List<GaoOrderFulfillmentItemRequest> itemRequests(String fulfillmentId) {
|
||||
return copyRequestItems(itemMapper.selectByFulfillmentId(fulfillmentId));
|
||||
}
|
||||
|
||||
private List<GaoOrderFulfillmentItemRequest> copyRequestItems(List<GaoOrderFulfillmentItem> itemList) {
|
||||
@@ -575,7 +509,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
for (GaoOrderFulfillmentItem item : TimiJava.safeIterable(itemList)) {
|
||||
GaoOrderFulfillmentItemRequest request = new GaoOrderFulfillmentItemRequest();
|
||||
request.setSourceOrderItemId(item.getSourceOrderItemId());
|
||||
request.setSourceCustomerPackageItemId(item.getSourceCustomerPackageItemId());
|
||||
request.setQuantity(item.getQuantity());
|
||||
result.add(request);
|
||||
}
|
||||
@@ -587,7 +520,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
result.setProductId(source.getProductId());
|
||||
result.setProductUnitId(source.getProductUnitId());
|
||||
result.setSourceOrderItemId(source.getSourceOrderItemId());
|
||||
result.setSourceCustomerPackageItemId(source.getSourceCustomerPackageItemId());
|
||||
result.setQuantity(source.getQuantity());
|
||||
result.setBaseQuantity(source.getBaseQuantity());
|
||||
result.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||
@@ -615,16 +547,14 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
&& (request.getDeliveredAt() == null || request.getDeliveredAt().equals(existing.getDeliveredAt()))
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 对应履约请求内容不一致");
|
||||
List<GaoOrderFulfillmentItem> existingItems = itemMapper.selectByFulfillmentId(existing.getId());
|
||||
List<GaoOrderFulfillmentItemRequest> requestItems = requestItems(request.getItemList());
|
||||
List<GaoOrderFulfillmentItemRequest> requestItems = request.getItemList();
|
||||
TimiException.requiredTrue(existingItems.size() == (requestItems == null ? 0 : requestItems.size()), "requestNo 对应履约请求内容不一致");
|
||||
Map<String, BigDecimal> requestMap = new HashMap<>();
|
||||
for (GaoOrderFulfillmentItemRequest item : TimiJava.safeIterable(requestItems)) {
|
||||
String key = hasText(item.getSourceOrderItemId()) ? "O:" + item.getSourceOrderItemId() : "P:" + item.getSourceCustomerPackageItemId();
|
||||
requestMap.put(key, item.getQuantity());
|
||||
requestMap.put(item.getSourceOrderItemId(), item.getQuantity());
|
||||
}
|
||||
for (GaoOrderFulfillmentItem item : existingItems) {
|
||||
String key = item.getSourceOrderItemId() == null ? "P:" + item.getSourceCustomerPackageItemId() : "O:" + item.getSourceOrderItemId();
|
||||
TimiException.requiredTrue(item.getQuantity().compareTo(requestMap.getOrDefault(key, ZERO)) == 0, "requestNo 对应履约请求内容不一致");
|
||||
TimiException.requiredTrue(item.getQuantity().compareTo(requestMap.getOrDefault(item.getSourceOrderItemId(), ZERO)) == 0, "requestNo 对应履约请求内容不一致");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,10 +571,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasText(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private String requiredText(String value, String message) {
|
||||
TimiException.required(value, message);
|
||||
String result = value.trim();
|
||||
@@ -710,13 +636,13 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
}
|
||||
}
|
||||
|
||||
private record SourceEntry(String sourceOrderItemId, String sourceCustomerPackageItemId, String productId, String productUnitId,
|
||||
private record SourceEntry(String sourceOrderItemId, String productId, String productUnitId,
|
||||
String productNameSnapshot, String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
|
||||
BigDecimal totalQuantity, BigDecimal reservedBefore, BigDecimal fulfilledBefore, GaoProduct product,
|
||||
GaoProductUnit unit, boolean packageSource) {
|
||||
GaoProductUnit unit) {
|
||||
|
||||
private String sourceId() {
|
||||
return packageSource ? sourceCustomerPackageItemId : sourceOrderItemId;
|
||||
return sourceOrderItemId;
|
||||
}
|
||||
|
||||
private GaoOrderFulfillmentItem toFulfillmentItem(String fulfillmentId) {
|
||||
@@ -725,7 +651,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
||||
result.setProductId(productId);
|
||||
result.setProductUnitId(productUnitId);
|
||||
result.setSourceOrderItemId(sourceOrderItemId);
|
||||
result.setSourceCustomerPackageItemId(sourceCustomerPackageItemId);
|
||||
result.setQuantity(quantity);
|
||||
result.setBaseQuantity(quantity.multiply(baseQuantityPerUnitSnapshot).setScale(3));
|
||||
result.setReservedBefore(reservedBefore);
|
||||
|
||||
+125
-57
@@ -7,7 +7,6 @@ import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||
@@ -18,7 +17,6 @@ import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoSalesOrderItemMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoSalesOrderMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
||||
@@ -55,6 +53,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -77,7 +76,6 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
private final GaoSalesOrderItemMapper itemMapper;
|
||||
private final GaoFundRecordService fundRecordService;
|
||||
private final ObjectProvider<GaoOrderFulfillmentService> fulfillmentServiceProvider;
|
||||
private final ObjectProvider<GaoCustomerPackageService> customerPackageServiceProvider;
|
||||
private final GaoCustomerService customerService;
|
||||
private final GaoProductService productService;
|
||||
private final GaoProductUnitService unitService;
|
||||
@@ -114,6 +112,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
TimiException.required(order, "not found sales order");
|
||||
storeScopeService.checkStoreId(order.getStoreId());
|
||||
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
fillItemBalance(item);
|
||||
}
|
||||
fillItemAttachments(itemList);
|
||||
order.setItemList(itemList);
|
||||
fillCustomer(List.of(order));
|
||||
@@ -177,26 +178,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
}
|
||||
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
||||
String operatorUserId = userLoginService.getRequireLoginUserId();
|
||||
GaoSalesOrder order = newOrder(storeId, customer.getId(), GaoSalesOrder.OrderType.DIRECT_PRODUCT, requestNo, purchasedAt, operatorUserId, request.getRemark());
|
||||
order.setSaleAmount(saleAmount);
|
||||
// 零元订单创建时即为全额实收
|
||||
boolean paidAtCreate = saleAmount == 0;
|
||||
if (paidAtCreate) {
|
||||
order.setPaymentStatus(GaoSalesOrder.PaymentStatus.PAID);
|
||||
order.setPaidAt(Time.now());
|
||||
} else {
|
||||
order.setPaymentStatus(GaoSalesOrder.PaymentStatus.UNPAID);
|
||||
}
|
||||
super.create(order);
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
item.setSalesOrderId(order.getId());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
GaoSalesOrder order = createOrder(storeId, customer.getId(), GaoSalesOrder.OrderType.DIRECT_PRODUCT, requestNo,
|
||||
purchasedAt, operatorUserId, request.getRemark(), saleAmount, itemList);
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList());
|
||||
order.setItemList(itemList);
|
||||
if (paidAtCreate) {
|
||||
pointTriggerService.onSalesOrderPaid(order);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
@@ -213,35 +197,25 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
TimiException.required(data, "not found package order data");
|
||||
String requestNo = requiredText(data.getRequestNo(), "not found requestNo");
|
||||
String storeId = requiredText(data.getStoreId(), "not found storeId");
|
||||
TimiException.required(data.getCustomerId(), "not found customerId");
|
||||
TimiException.required(data.getPackageConfigId(), "not found packageConfigId");
|
||||
TimiException.required(data.getSaleAmount(), "not found saleAmount");
|
||||
storeScopeService.checkStoreId(storeId);
|
||||
GaoSalesOrder existing = mapper.selectByRequestNo(storeId, requestNo);
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getOrderType() == GaoSalesOrder.OrderType.PACKAGE
|
||||
&& data.getCustomerId().equals(existing.getCustomerId())
|
||||
&& data.getSaleAmount().equals(existing.getSaleAmount()), "requestNo 已被其他订单使用");
|
||||
return existing;
|
||||
verifyExistingPackageItems(existing, data.getItemList());
|
||||
return detail(existing.getId());
|
||||
}
|
||||
GaoCustomer customer = customerService.get(data.getCustomerId());
|
||||
TimiException.required(customer, "not found customer");
|
||||
TimiException.requiredTrue(storeId.equals(customer.getStoreId()), "客户不属于当前门店");
|
||||
TimiException.required(data.getPackageConfigId(), "not found packageConfigId");
|
||||
TimiException.requiredTrue(0 <= data.getSaleAmount(), "成交价不能为负数");
|
||||
GaoSalesOrder order = newOrder(storeId, customer.getId(), GaoSalesOrder.OrderType.PACKAGE, requestNo,
|
||||
TimiJava.defaultIfNull(data.getPurchasedAt(), Time.now()), data.getOperatorUserId(), data.getRemark());
|
||||
order.setSaleAmount(data.getSaleAmount());
|
||||
// 零元订单创建时即为全额实收
|
||||
boolean paidAtCreate = data.getSaleAmount() == 0;
|
||||
if (paidAtCreate) {
|
||||
order.setPaymentStatus(GaoSalesOrder.PaymentStatus.PAID);
|
||||
order.setPaidAt(Time.now());
|
||||
} else {
|
||||
order.setPaymentStatus(GaoSalesOrder.PaymentStatus.UNPAID);
|
||||
}
|
||||
super.create(order);
|
||||
if (paidAtCreate) {
|
||||
pointTriggerService.onSalesOrderPaid(order);
|
||||
}
|
||||
return order;
|
||||
return createOrder(storeId, customer.getId(), GaoSalesOrder.OrderType.PACKAGE, requestNo,
|
||||
TimiJava.defaultIfNull(data.getPurchasedAt(), Time.now()), data.getOperatorUserId(), data.getRemark(),
|
||||
data.getSaleAmount(), data.getItemList());
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@@ -265,26 +239,16 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
TimiException.requiredTrue(fulfillmentServiceProvider.getObject().countPendingBySalesOrderId(order.getId()) == 0, "订单存在待履约操作,不能取消");
|
||||
String userId = userLoginService.getRequireLoginUserId();
|
||||
GaoSalesOrder.RefundStatus refundStatus = safe(order.getPaidAmount()) == 0 ? GaoSalesOrder.RefundStatus.REFUNDED : GaoSalesOrder.RefundStatus.PENDING;
|
||||
Long cancelledAt = Time.now();
|
||||
TimiException.requiredTrue(1 == mapper.cancelIf(order.getId(), GaoSalesOrder.Status.ACTIVE, GaoSalesOrder.Status.CANCELLED,
|
||||
Time.now(), userId, reason, refundStatus), "订单状态已变化");
|
||||
cancelledAt, userId, reason, refundStatus), "订单状态已变化");
|
||||
// 订单取消立即失去有效消费资格,不等待退款完成
|
||||
pointTriggerService.revokeBySalesOrder(order, reason);
|
||||
order.setStatus(GaoSalesOrder.Status.CANCELLED);
|
||||
order.setCancelledAt(Time.now());
|
||||
order.setCancelledAt(cancelledAt);
|
||||
order.setCancelOperatorUserId(userId);
|
||||
order.setCancelReason(reason);
|
||||
order.setRefundStatus(refundStatus);
|
||||
if (order.getOrderType() == GaoSalesOrder.OrderType.PACKAGE) {
|
||||
GaoCustomerPackageService customerPackageService = customerPackageServiceProvider.getObject();
|
||||
GaoCustomerPackage packageEntity = customerPackageService.getBySalesOrderId(order.getId());
|
||||
if (packageEntity != null && packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE) {
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.CANCELLED);
|
||||
packageEntity.setCancelledAt(order.getCancelledAt());
|
||||
packageEntity.setCancelOperatorUserId(userId);
|
||||
packageEntity.setCancelReason(reason);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
@@ -342,16 +306,13 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
return;
|
||||
}
|
||||
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
||||
boolean fulfilled = false;
|
||||
if (order.getOrderType() == GaoSalesOrder.OrderType.DIRECT_PRODUCT) {
|
||||
fulfilled = !itemList.isEmpty();
|
||||
boolean fulfilled = !itemList.isEmpty();
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
if (decimal(item.getFulfilledQuantity()).compareTo(decimal(item.getQuantity())) < 0) {
|
||||
fulfilled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateFulfillmentStatus(order, fulfilled);
|
||||
}
|
||||
|
||||
@@ -374,6 +335,16 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
return itemMapper.selectBySalesOrderId(orderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, GaoSalesOrder> mapByIdList(Collection<String> idList) {
|
||||
if (idList == null || idList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
List<GaoSalesOrder> orderList = mapper.selectByIdList(idList);
|
||||
fillItemList(orderList);
|
||||
return orderList.stream().collect(java.util.stream.Collectors.toMap(GaoSalesOrder::getId, java.util.function.Function.identity()));
|
||||
}
|
||||
|
||||
private List<GaoSalesOrderItem> resolveDirectItems(String storeId, List<GaoSalesOrderItemRequest> sourceList) {
|
||||
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "直接产品订单至少需要一个产品");
|
||||
Set<String> productUnitKeySet = new HashSet<>();
|
||||
@@ -401,6 +372,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
||||
item.setProductId(product.getId());
|
||||
item.setProductUnitId(unit.getId());
|
||||
item.setItemType(GaoSalesOrderItem.ItemType.DIRECT_PRODUCT);
|
||||
item.setProductNameSnapshot(product.getName());
|
||||
item.setUnitSnapshot(unit.getName());
|
||||
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
|
||||
@@ -456,6 +428,83 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
return order;
|
||||
}
|
||||
|
||||
private GaoSalesOrder createOrder(String storeId, String customerId, GaoSalesOrder.OrderType orderType, String requestNo,
|
||||
Long purchasedAt, String operatorUserId, String remark, long saleAmount, List<GaoSalesOrderItem> itemList) {
|
||||
TimiException.requiredTrue(itemList != null && !itemList.isEmpty(), "销售订单至少需要一个订单明细");
|
||||
TimiException.requiredTrue(0 <= saleAmount, "成交价不能为负数");
|
||||
GaoSalesOrderItem.ItemType expectedItemType = orderType == GaoSalesOrder.OrderType.PACKAGE
|
||||
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
||||
Set<String> productUnitKeySet = new HashSet<>();
|
||||
long lineAmountSum = 0L;
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
TimiException.required(item, "销售订单明细不能为空");
|
||||
TimiException.requiredTrue(item.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||
TimiException.required(item.getProductId(), "订单项产品不能为空");
|
||||
TimiException.required(item.getProductUnitId(), "订单项单位不能为空");
|
||||
TimiException.requiredTrue(productUnitKeySet.add(item.getProductId() + "|" + item.getProductUnitId()),
|
||||
"同一订单不能重复产品和单位");
|
||||
TimiException.required(item.getQuantity(), "订单项数量不能为空");
|
||||
TimiException.requiredTrue(0 < item.getQuantity().signum(), "订单项数量必须大于 0");
|
||||
TimiException.requiredTrue(3 >= item.getQuantity().stripTrailingZeros().scale(), "订单项数量最多保留三位小数");
|
||||
TimiException.required(item.getBaseQuantityPerUnitSnapshot(), "订单项单位换算快照不能为空");
|
||||
TimiException.requiredTrue(0 < item.getBaseQuantityPerUnitSnapshot().signum(), "订单项单位换算快照必须大于 0");
|
||||
TimiException.requiredTrue(item.getReservedQuantity() != null && item.getFulfilledQuantity() != null,
|
||||
"订单项履约数量不能为空");
|
||||
TimiException.requiredTrue(0 <= item.getReservedQuantity().signum() && 0 <= item.getFulfilledQuantity().signum(),
|
||||
"订单项履约数量不能为负数");
|
||||
TimiException.requiredTrue(item.getReservedQuantity().add(item.getFulfilledQuantity()).compareTo(item.getQuantity()) <= 0,
|
||||
"订单项履约数量不能超过购买数量");
|
||||
if (expectedItemType == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT) {
|
||||
TimiException.requiredNull(item.getUnitPrice(), "套餐订单项不能填写单价");
|
||||
TimiException.requiredNull(item.getLineAmount(), "套餐订单项不能填写行金额");
|
||||
} else {
|
||||
TimiException.required(item.getUnitPrice(), "直接产品订单项单价不能为空");
|
||||
TimiException.required(item.getLineAmount(), "直接产品订单项行金额不能为空");
|
||||
TimiException.requiredTrue(0 <= item.getUnitPrice() && 0 <= item.getLineAmount(), "直接产品订单金额不能为负数");
|
||||
lineAmountSum = Math.addExact(lineAmountSum, item.getLineAmount());
|
||||
}
|
||||
}
|
||||
if (expectedItemType == GaoSalesOrderItem.ItemType.DIRECT_PRODUCT) {
|
||||
TimiException.requiredTrue(lineAmountSum == saleAmount, "直接产品订单行金额合计必须等于成交价");
|
||||
}
|
||||
GaoSalesOrder order = newOrder(storeId, customerId, orderType, requestNo, purchasedAt, operatorUserId, remark);
|
||||
order.setSaleAmount(saleAmount);
|
||||
boolean paidAtCreate = saleAmount == 0;
|
||||
order.setPaymentStatus(paidAtCreate ? GaoSalesOrder.PaymentStatus.PAID : GaoSalesOrder.PaymentStatus.UNPAID);
|
||||
if (paidAtCreate) {
|
||||
order.setPaidAt(Time.now());
|
||||
}
|
||||
super.create(order);
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
item.setSalesOrderId(order.getId());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
order.setItemList(itemList);
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
fillItemBalance(item);
|
||||
}
|
||||
if (paidAtCreate) {
|
||||
pointTriggerService.onSalesOrderPaid(order);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
private void verifyExistingPackageItems(GaoSalesOrder existing, List<GaoSalesOrderItem> requestItemList) {
|
||||
TimiException.requiredTrue(requestItemList != null && !requestItemList.isEmpty(), "销售订单至少需要一个订单明细");
|
||||
List<GaoSalesOrderItem> existingItemList = itemMapper.selectBySalesOrderId(existing.getId());
|
||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致");
|
||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), java.util.function.Function.identity()));
|
||||
for (GaoSalesOrderItem requestItem : requestItemList) {
|
||||
GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getProductId() + "|" + requestItem.getProductUnitId());
|
||||
TimiException.required(existingItem, "requestNo 对应订单内容不一致");
|
||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||
&& existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0
|
||||
&& existingItem.getSort().equals(requestItem.getSort())
|
||||
&& sameText(existingItem.getRemark(), requestItem.getRemark()), "requestNo 对应订单内容不一致");
|
||||
}
|
||||
}
|
||||
|
||||
private void fillItemList(List<GaoSalesOrder> orderList) {
|
||||
if (orderList == null || orderList.isEmpty()) {
|
||||
return;
|
||||
@@ -479,9 +528,24 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
itemList.addAll(orderItemList);
|
||||
order.setUnpaidAmount(safe(order.getSaleAmount()) - safe(order.getPaidAmount()));
|
||||
}
|
||||
for (GaoSalesOrderItem item : itemList) {
|
||||
fillItemBalance(item);
|
||||
}
|
||||
fillItemAttachments(itemList);
|
||||
}
|
||||
|
||||
/// 填充订单项履约余额投影
|
||||
///
|
||||
/// @param item 订单项
|
||||
private void fillItemBalance(GaoSalesOrderItem item) {
|
||||
BigDecimal quantity = decimal(item.getQuantity());
|
||||
BigDecimal reserved = decimal(item.getReservedQuantity());
|
||||
BigDecimal fulfilled = decimal(item.getFulfilledQuantity());
|
||||
item.setPendingQuantity(reserved);
|
||||
item.setAvailableQuantity(quantity.subtract(reserved).subtract(fulfilled));
|
||||
item.setRemainingQuantity(quantity.subtract(fulfilled));
|
||||
}
|
||||
|
||||
private void fillItemAttachments(List<GaoSalesOrderItem> itemList) {
|
||||
if (itemList == null || itemList.isEmpty()) {
|
||||
return;
|
||||
@@ -552,6 +616,10 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean sameText(String left, String right) {
|
||||
return left == null ? right == null : left.equals(right);
|
||||
}
|
||||
|
||||
private long safe(Long value) {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -22,7 +22,7 @@ public class GaoCustomerPackagePage extends BasePage {
|
||||
/// 客户或套餐关键词
|
||||
private String keyword;
|
||||
|
||||
/// 套餐状态
|
||||
private GaoCustomerPackage.Status status;
|
||||
/// 关联订单状态
|
||||
private GaoSalesOrder.Status status;
|
||||
|
||||
}
|
||||
|
||||
@@ -11,12 +11,9 @@ import java.math.BigDecimal;
|
||||
@Data
|
||||
public class GaoOrderFulfillmentItemRequest {
|
||||
|
||||
/// 直接订单明细 ID,与客户套餐权益 ID 二选一
|
||||
/// 来源销售订单明细 ID
|
||||
private String sourceOrderItemId;
|
||||
|
||||
/// 客户套餐权益项 ID,与直接订单明细 ID 二选一
|
||||
private String sourceCustomerPackageItemId;
|
||||
|
||||
/// 本次履约数量
|
||||
private BigDecimal quantity;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@ public class GaoOrderFulfillmentPage extends BasePage {
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 履约类型
|
||||
private GaoOrderFulfillment.FulfillmentType fulfillmentType;
|
||||
|
||||
/// 履约状态
|
||||
private GaoOrderFulfillment.Status status;
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐订单内部创建数据
|
||||
///
|
||||
/// @author Codex
|
||||
@@ -38,4 +41,7 @@ public class GaoPackageOrderCreateData {
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 订单可履约明细
|
||||
private List<GaoSalesOrderItem> itemList;
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
ALTER TABLE `gao_sales_order_item`
|
||||
ADD COLUMN `item_type` VARCHAR(32) NOT NULL DEFAULT 'DIRECT_PRODUCT' COMMENT 'DIRECT_PRODUCT 直接产品,PACKAGE_ENTITLEMENT 套餐权益' AFTER `sales_order_id`,
|
||||
MODIFY COLUMN `unit_price` BIGINT NULL COMMENT '实际单价,套餐权益为空',
|
||||
MODIFY COLUMN `line_amount` BIGINT NULL COMMENT '行金额,套餐权益为空';
|
||||
|
||||
ALTER TABLE `gao_order_fulfillment_item`
|
||||
DROP KEY `uk_gao_order_fulfillment_item_package`,
|
||||
DROP KEY `idx_gao_order_fulfillment_item_package_source`,
|
||||
MODIFY COLUMN `source_order_item_id` VARCHAR(36) NOT NULL COMMENT '来源销售订单项 ID',
|
||||
DROP COLUMN `source_customer_package_item_id`;
|
||||
|
||||
ALTER TABLE `gao_order_fulfillment`
|
||||
DROP COLUMN `fulfillment_type`;
|
||||
|
||||
ALTER TABLE `gao_customer_package`
|
||||
DROP KEY `idx_gao_customer_package_customer`,
|
||||
DROP COLUMN `status`,
|
||||
DROP COLUMN `cancelled_at`,
|
||||
DROP COLUMN `cancel_operator_user_id`,
|
||||
DROP COLUMN `cancel_reason`;
|
||||
|
||||
DROP TABLE IF EXISTS `gao_customer_package_item`;
|
||||
|
||||
DELETE role_permission
|
||||
FROM `role_permission` role_permission
|
||||
INNER JOIN `permission` permission ON permission.`id` = role_permission.`permission_id`
|
||||
WHERE permission.`module_code` = 'GAO'
|
||||
AND permission.`code` IN ('PACKAGE_DELIVERY:CREATE', 'PACKAGE_DELIVERY:READ', 'PACKAGE_DELIVERY:VERIFY', 'PACKAGE_DELIVERY:REVOKE');
|
||||
|
||||
DELETE role_permission_delegation
|
||||
FROM `role_permission_delegation` role_permission_delegation
|
||||
INNER JOIN `permission` permission ON permission.`id` = role_permission_delegation.`permission_id`
|
||||
WHERE permission.`module_code` = 'GAO'
|
||||
AND permission.`code` IN ('PACKAGE_DELIVERY:CREATE', 'PACKAGE_DELIVERY:READ', 'PACKAGE_DELIVERY:VERIFY', 'PACKAGE_DELIVERY:REVOKE');
|
||||
|
||||
DELETE FROM `permission`
|
||||
WHERE `module_code` = 'GAO'
|
||||
AND `code` IN ('PACKAGE_DELIVERY:CREATE', 'PACKAGE_DELIVERY:READ', 'PACKAGE_DELIVERY:VERIFY', 'PACKAGE_DELIVERY:REVOKE');
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageItemMapper">
|
||||
<select id="selectByCustomerPackageId" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem">
|
||||
SELECT * FROM `gao_customer_package_item`
|
||||
WHERE `customer_package_id` = #{customerPackageId} AND `deleted_at` IS NULL
|
||||
ORDER BY `sort`, `created_at`
|
||||
</select>
|
||||
<select id="selectByCustomerPackageIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem">
|
||||
SELECT * FROM `gao_customer_package_item`
|
||||
WHERE `deleted_at` IS NULL
|
||||
<choose>
|
||||
<when test="customerPackageIdList != null and !customerPackageIdList.isEmpty()">
|
||||
AND `customer_package_id` IN <foreach collection="customerPackageIdList" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
</when>
|
||||
<otherwise>AND FALSE</otherwise>
|
||||
</choose>
|
||||
ORDER BY `customer_package_id`, `sort`, `created_at`
|
||||
</select>
|
||||
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem">
|
||||
SELECT * FROM `gao_customer_package_item`
|
||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
||||
FOR UPDATE
|
||||
</select>
|
||||
<update id="updateReservedQuantity">
|
||||
UPDATE `gao_customer_package_item`
|
||||
SET `reserved_quantity` = `reserved_quantity` + #{delta}, `updated_at` = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000)
|
||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
||||
AND `reserved_quantity` + #{delta} >= 0
|
||||
AND `reserved_quantity` + `fulfilled_quantity` + #{delta} <= `total_quantity`
|
||||
</update>
|
||||
<update id="updateFulfilledQuantity">
|
||||
UPDATE `gao_customer_package_item`
|
||||
SET `fulfilled_quantity` = `fulfilled_quantity` + #{delta}, `updated_at` = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000)
|
||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
||||
AND `fulfilled_quantity` + #{delta} >= 0
|
||||
AND `reserved_quantity` + `fulfilled_quantity` + #{delta} <= `total_quantity`
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -1,11 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper">
|
||||
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackage">
|
||||
SELECT * FROM `gao_customer_package`
|
||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
||||
FOR UPDATE
|
||||
</select>
|
||||
<select id="selectBySalesOrderId" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackage">
|
||||
SELECT * FROM `gao_customer_package`
|
||||
WHERE `sales_order_id` = #{salesOrderId} AND `deleted_at` IS NULL
|
||||
@@ -15,18 +10,19 @@
|
||||
p.`deleted_at` IS NULL
|
||||
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||
<if test="customerId != null and customerId != ''">AND p.`customer_id` = #{customerId}</if>
|
||||
<if test="status != null">AND p.`status` = #{status}</if>
|
||||
<if test="status != null">AND o.`status` = #{status}</if>
|
||||
<if test="keyword != null and keyword.trim() != ''">AND (p.`package_name_snapshot` LIKE CONCAT('%', #{keyword}, '%') OR c.`name` LIKE CONCAT('%', #{keyword}, '%') OR c.`code` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||
</sql>
|
||||
<select id="countByQuery" resultType="long">
|
||||
SELECT COUNT(1) FROM `gao_customer_package` p
|
||||
LEFT JOIN `gao_customer` c ON c.`id` = p.`customer_id`
|
||||
INNER JOIN `gao_sales_order` o ON o.`id` = p.`sales_order_id` AND o.`store_id` = p.`store_id` AND o.`deleted_at` IS NULL
|
||||
WHERE <include refid="queryWhere"/>
|
||||
</select>
|
||||
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomerPackage">
|
||||
SELECT p.* FROM `gao_customer_package` p
|
||||
LEFT JOIN `gao_customer` c ON c.`id` = p.`customer_id`
|
||||
LEFT JOIN `gao_sales_order` o ON o.`id` = p.`sales_order_id` AND o.`store_id` = p.`store_id` AND o.`deleted_at` IS NULL
|
||||
INNER JOIN `gao_sales_order` o ON o.`id` = p.`sales_order_id` AND o.`store_id` = p.`store_id` AND o.`deleted_at` IS NULL
|
||||
WHERE <include refid="queryWhere"/>
|
||||
ORDER BY o.`purchased_at` DESC, p.`created_at` DESC
|
||||
LIMIT #{offset}, #{limit}
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<if test="storeId != null and storeId != ''">AND `store_id` = #{storeId}</if>
|
||||
<if test="salesOrderId != null and salesOrderId != ''">AND `sales_order_id` = #{salesOrderId}</if>
|
||||
<if test="customerId != null and customerId != ''">AND `customer_id` = #{customerId}</if>
|
||||
<if test="fulfillmentType != null">AND `fulfillment_type` = #{fulfillmentType}</if>
|
||||
<if test="status != null">AND `status` = #{status}</if>
|
||||
<if test="beginAt != null">AND `delivered_at` >= #{beginAt}</if>
|
||||
<if test="endAt != null">AND `delivered_at` <= #{endAt}</if>
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
||||
FOR UPDATE
|
||||
</select>
|
||||
<select id="selectByIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoSalesOrder">
|
||||
SELECT * FROM `gao_sales_order`
|
||||
WHERE `id` IN
|
||||
<foreach collection="idList" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
AND `deleted_at` IS NULL
|
||||
</select>
|
||||
<sql id="queryWhere">
|
||||
o.`deleted_at` IS NULL
|
||||
<if test="storeId != null and storeId != ''">AND o.`store_id` = #{storeId}</if>
|
||||
|
||||
Reference in New Issue
Block a user