Merge branch 'master' of http://git.lionquexpress.com/wuweihua/BMS
This commit is contained in:
+33
-2
@@ -1,12 +1,17 @@
|
||||
package com.ruoyi.web.controller.ar;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -31,6 +36,7 @@ import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
@@ -49,12 +55,23 @@ public class ArBillController extends BaseController {
|
||||
@Autowired
|
||||
private IArBillService billService;
|
||||
|
||||
/**
|
||||
* 查询参数中的日期必须严格按 yyyy-MM-dd 解析,禁止非法日期自动滚动到其他日期。
|
||||
*/
|
||||
@InitBinder
|
||||
public void initDateBinder(WebDataBinder binder) {
|
||||
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
||||
formatter.setLenient(false);
|
||||
binder.registerCustomEditor(Date.class, new CustomDateEditor(formatter, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询账单列表;分页参数由 BaseController.startPage 交给 MyBatis PageHelper 处理。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ArBillQuery query) {
|
||||
public TableDataInfo list(ArBillQuery query, HttpServletRequest request) {
|
||||
rejectMultiValueParameter(request, "customerId");
|
||||
startPage();
|
||||
List<ArBill> list = billService.selectArBillList(query);
|
||||
return getDataTable(list);
|
||||
@@ -104,7 +121,8 @@ public class ArBillController extends BaseController {
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/generate/preview")
|
||||
public AjaxResult preview(ArBillGenerateQuery query) {
|
||||
public AjaxResult preview(ArBillGenerateQuery query, HttpServletRequest request) {
|
||||
rejectMultiValueParameter(request, "customerId");
|
||||
startPage();
|
||||
List<PrcOrderBill> list = billService.previewBill(query);
|
||||
TableDataInfo pageData = getDataTable(list);
|
||||
@@ -252,4 +270,17 @@ public class ArBillController extends BaseController {
|
||||
if (StringUtils.isNotEmpty(headerRequestId)) return headerRequestId;
|
||||
return IdUtils.fastSimpleUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止把 customerId 以数组参数或重复参数提交,避免参数绑定失败后悄悄退化为无客户条件查询。
|
||||
*/
|
||||
private void rejectMultiValueParameter(HttpServletRequest request, String parameterName) {
|
||||
if (request.getParameterMap().containsKey(parameterName + "[]")) {
|
||||
throw new ServiceException("查询参数[" + parameterName + "]格式不正确,请传入单个客户ID");
|
||||
}
|
||||
String[] values = request.getParameterValues(parameterName);
|
||||
if (values != null && values.length > 1) {
|
||||
throw new ServiceException("查询参数[" + parameterName + "]只能传入一个值");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.ruoyi.common.config.serializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
|
||||
|
||||
/**
|
||||
* 严格解析 yyyy-MM-dd 日期,避免 SimpleDateFormat 的宽松解析把非法日期自动滚动成其他日期。
|
||||
*/
|
||||
public class StrictDateDeserializer extends JsonDeserializer<Date>
|
||||
{
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")
|
||||
.withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
@Override
|
||||
public Date deserialize(JsonParser parser, DeserializationContext context) throws IOException
|
||||
{
|
||||
if (!parser.hasToken(JsonToken.VALUE_STRING))
|
||||
{
|
||||
throw InvalidFormatException.from(parser, "日期必须使用 yyyy-MM-dd 格式", parser.getValueAsString(), Date.class);
|
||||
}
|
||||
|
||||
String value = parser.getText();
|
||||
if (value == null || value.trim().isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LocalDate date = LocalDate.parse(value.trim(), FORMATTER);
|
||||
return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
catch (DateTimeParseException ex)
|
||||
{
|
||||
throw InvalidFormatException.from(parser, "日期必须是有效的 yyyy-MM-dd 日期", value, Date.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-12
@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingPathVariableException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
@@ -13,11 +14,9 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.exception.DemoModeException;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.html.EscapeUtil;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
@@ -81,13 +80,19 @@ public class GlobalExceptionHandler
|
||||
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
String value = Convert.toStr(e.getValue());
|
||||
if (StringUtils.isNotEmpty(value))
|
||||
{
|
||||
value = EscapeUtil.clean(value);
|
||||
}
|
||||
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
|
||||
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
|
||||
return AjaxResult.error(String.format("请求参数[%s]格式不正确,请检查输入内容", e.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求体无法解析时只返回用户可处理的提示,避免把 Jackson 或数据库内部信息直接返回前端。
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public AjaxResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e,
|
||||
HttpServletRequest request)
|
||||
{
|
||||
log.error("请求地址'{}',请求体格式不正确.", request.getRequestURI(), e);
|
||||
return AjaxResult.error("请求参数格式不正确,请检查日期、金额等字段");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +103,7 @@ public class GlobalExceptionHandler
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||
return AjaxResult.error(e.getMessage());
|
||||
return AjaxResult.error("系统处理异常,请稍后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +114,7 @@ public class GlobalExceptionHandler
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||
return AjaxResult.error(e.getMessage());
|
||||
return AjaxResult.error("系统处理异常,请稍后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +124,7 @@ public class GlobalExceptionHandler
|
||||
public AjaxResult handleBindException(BindException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
String message = e.getAllErrors().get(0).getDefaultMessage();
|
||||
String message = e.getAllErrors().isEmpty() ? "请求参数校验失败" : e.getAllErrors().get(0).getDefaultMessage();
|
||||
return AjaxResult.error(message);
|
||||
}
|
||||
|
||||
@@ -130,7 +135,8 @@ public class GlobalExceptionHandler
|
||||
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
String message = e.getBindingResult().getFieldError().getDefaultMessage();
|
||||
String message = e.getBindingResult().getFieldError() == null
|
||||
? "请求参数校验失败" : e.getBindingResult().getFieldError().getDefaultMessage();
|
||||
return AjaxResult.error(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ public class ArBillDetail implements Serializable {
|
||||
private Integer detailNo;
|
||||
private String orderNo;
|
||||
private String trackingNo;
|
||||
/** 转单号快照;账单生成时从来源计费订单复制,保证历史账单可追溯。 */
|
||||
private String transferNo;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date receiveTime;
|
||||
private String productCode;
|
||||
|
||||
@@ -17,9 +17,9 @@ public class ArBillGenerateQuery
|
||||
|
||||
private String currency;
|
||||
|
||||
/** 订单号或运单号关键词,按 OR 条件模糊查询。 */
|
||||
/** 订单号、运单号或转单号关键词,按 OR 条件模糊查询。 */
|
||||
private String orderOrTrackingNo;
|
||||
|
||||
/** 全选模式下,排除弹窗使用的附加订单号或运单号关键词。 */
|
||||
/** 全选模式下,排除弹窗使用的附加订单号、运单号或转单号关键词。 */
|
||||
private String excludeOrderOrTrackingNo;
|
||||
}
|
||||
|
||||
+8
-1
@@ -3,8 +3,11 @@ package com.ruoyi.ar.domain.vo;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import com.ruoyi.common.config.serializer.StrictDateDeserializer;
|
||||
import com.ruoyi.ar.enums.ArBillSelectionMode;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -15,11 +18,13 @@ public class ArBillGenerateRequest
|
||||
@NotNull(message = "客户不能为空")
|
||||
private Long customerId;
|
||||
@NotBlank(message = "账单名称不能为空")
|
||||
@Size(max = 100, message = "账单名称不能超过100个字符")
|
||||
private String billName;
|
||||
private String customerCode;
|
||||
private String customerName;
|
||||
@NotNull(message = "账单截止日不能为空")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonDeserialize(using = StrictDateDeserializer.class)
|
||||
private Date billPeriodEnd;
|
||||
/** 手动模式选中的来源订单 ID;全选模式为空。 */
|
||||
private List<String> sourceBillIds;
|
||||
@@ -28,13 +33,15 @@ public class ArBillGenerateRequest
|
||||
/** 全选模式中用户明确排除的来源订单 ID。 */
|
||||
private List<String> excludedSourceBillIds;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonDeserialize(using = StrictDateDeserializer.class)
|
||||
private Date billDate;
|
||||
private String currency = "CNY";
|
||||
/** 订单号或运单号关键词,生成时用于再次校验筛选条件。 */
|
||||
/** 订单号、运单号或转单号关键词,生成时用于再次校验筛选条件。 */
|
||||
private String orderOrTrackingNo;
|
||||
private String bankAccountName;
|
||||
private String bankAccountNo;
|
||||
private String bankName;
|
||||
@Size(max = 500, message = "备注不能超过500个字符")
|
||||
private String remark;
|
||||
/** 前端幂等号;重复提交同一请求时复用原任务。 */
|
||||
private String requestId;
|
||||
|
||||
+3
@@ -90,6 +90,9 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService {
|
||||
throw new ServiceException("核销金额必须大于0");
|
||||
}
|
||||
amount = amount.setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("核销金额保留两位小数后必须大于0");
|
||||
}
|
||||
|
||||
// 1. 行锁锁定账单(加锁顺序:账单 -> 账户,与反核销保持一致)
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(cmd.getArBillId());
|
||||
|
||||
+11
-2
@@ -984,6 +984,7 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
detail.setDetailNo(detailNo);
|
||||
detail.setOrderNo(source.getOrderNo());
|
||||
detail.setTrackingNo(source.getTrackingNo());
|
||||
detail.setTransferNo(source.getTransferNo());
|
||||
detail.setReceiveTime(source.getOrderTime());
|
||||
detail.setProductCode(source.getProductCode());
|
||||
detail.setProductName(source.getProductName());
|
||||
@@ -1631,15 +1632,23 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
}
|
||||
|
||||
private void validatePeriod(ArBillGenerateRequest request) {
|
||||
if (request == null || request.getCustomerId() == null || StringUtils.isEmpty(request.getBillName())
|
||||
if (request == null || request.getCustomerId() == null || request.getCustomerId() <= 0
|
||||
|| StringUtils.isEmpty(request.getBillName())
|
||||
|| request.getBillName().trim().isEmpty()
|
||||
|| request.getBillPeriodEnd() == null) {
|
||||
throw new ServiceException("客户、账单名称和账单截止日不能为空");
|
||||
}
|
||||
if (request.getBillName().trim().length() > 100) {
|
||||
throw new ServiceException("账单名称不能超过100个字符");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(request.getRemark()) && request.getRemark().trim().length() > 500) {
|
||||
throw new ServiceException("备注不能超过500个字符");
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePreviewQuery(ArBillGenerateQuery query) {
|
||||
if (query == null || query.getCustomerId() == null || query.getBillPeriodEnd() == null) {
|
||||
if (query == null || query.getCustomerId() == null || query.getCustomerId() <= 0
|
||||
|| query.getBillPeriodEnd() == null) {
|
||||
throw new ServiceException("客户和账单截止日不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="detailNo" column="detail_no"/>
|
||||
<result property="orderNo" column="order_no"/>
|
||||
<result property="trackingNo" column="tracking_no"/>
|
||||
<result property="transferNo" column="transfer_no"/>
|
||||
<result property="receiveTime" column="receive_time"/>
|
||||
<result property="productCode" column="product_code"/>
|
||||
<result property="productName" column="product_name"/>
|
||||
@@ -32,7 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByBillId" parameterType="long" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, receive_time,
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight, volume_weight, charge_weight, pkg_length, pkg_width, pkg_height,
|
||||
currency, total_amount, remark, create_by, create_time
|
||||
@@ -40,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<select id="selectByBillIdAndSourceIds" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, receive_time,
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight, volume_weight, charge_weight, pkg_length, pkg_width, pkg_height,
|
||||
currency, total_amount, remark, create_by, create_time
|
||||
@@ -76,7 +77,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByBillIdWithFees" parameterType="long" resultMap="ArBillDetailWithFeesResult">
|
||||
select d.detail_id, d.ar_bill_id, d.prc_order_bill_id, d.detail_no, d.order_no, d.tracking_no, d.receive_time,
|
||||
select d.detail_id, d.ar_bill_id, d.prc_order_bill_id, d.detail_no, d.order_no, d.tracking_no, d.transfer_no, d.receive_time,
|
||||
d.product_code, d.product_name, d.goods_description, d.country_code, d.country_name, d.dest_postcode,
|
||||
d.actual_weight, d.volume_weight, d.charge_weight, d.pkg_length, d.pkg_width, d.pkg_height,
|
||||
d.currency, d.total_amount, d.remark, d.create_by, d.create_time,
|
||||
@@ -94,7 +95,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<select id="selectExportPage" resultMap="ArBillDetailResult">
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, receive_time,
|
||||
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight, volume_weight, charge_weight, pkg_length, pkg_width, pkg_height,
|
||||
currency, total_amount, remark, create_by, create_time
|
||||
@@ -105,14 +106,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ArBillDetail" useGeneratedKeys="true" keyProperty="detailId">
|
||||
insert into ar_bill_detail (ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, receive_time,
|
||||
insert into ar_bill_detail (ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
|
||||
product_code,
|
||||
product_name, goods_description, country_code, country_name, dest_postcode,
|
||||
actual_weight,
|
||||
volume_weight, charge_weight, pkg_length, pkg_width, pkg_height, currency,
|
||||
total_amount,
|
||||
remark, create_by, create_time)
|
||||
values (#{arBillId}, #{prcOrderBillId}, #{detailNo}, #{orderNo}, #{trackingNo}, #{receiveTime},
|
||||
values (#{arBillId}, #{prcOrderBillId}, #{detailNo}, #{orderNo}, #{trackingNo}, #{transferNo}, #{receiveTime},
|
||||
#{productCode}, #{productName}, #{goodsDescription}, #{countryCode}, #{countryName},
|
||||
#{destPostcode}, #{actualWeight}, #{volumeWeight}, #{chargeWeight}, #{pkgLength}, #{pkgWidth},
|
||||
#{pkgHeight}, #{currency}, #{totalAmount}, #{remark}, #{createBy}, sysdate())
|
||||
@@ -120,14 +121,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<insert id="insertBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="detailId">
|
||||
insert into ar_bill_detail (
|
||||
ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, receive_time, product_code,
|
||||
ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time, product_code,
|
||||
product_name, goods_description, country_code, country_name, dest_postcode, actual_weight,
|
||||
volume_weight, charge_weight, pkg_length, pkg_width, pkg_height, currency, total_amount,
|
||||
remark, create_by, create_time
|
||||
) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.arBillId}, #{item.prcOrderBillId}, #{item.detailNo}, #{item.orderNo}, #{item.trackingNo},
|
||||
#{item.receiveTime}, #{item.productCode}, #{item.productName}, #{item.goodsDescription},
|
||||
#{item.transferNo}, #{item.receiveTime}, #{item.productCode}, #{item.productName}, #{item.goodsDescription},
|
||||
#{item.countryCode}, #{item.countryName}, #{item.destPostcode}, #{item.actualWeight},
|
||||
#{item.volumeWeight}, #{item.chargeWeight}, #{item.pkgLength}, #{item.pkgWidth}, #{item.pkgHeight},
|
||||
#{item.currency}, #{item.totalAmount}, #{item.remark}, #{item.createBy}, sysdate())
|
||||
|
||||
@@ -225,7 +225,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<!-- rename_bill_tables.sql 已将需求文档中的 prc_bill 映射为本项目的 prc_order_bill。 -->
|
||||
<!-- 生成弹窗预览:按客户、截止日、币种和订单/运单号 OR 条件分页查询未入账订单。 -->
|
||||
<!-- 生成弹窗预览:按客户、截止日、币种和订单号/运单号/转单号 OR 条件分页查询未入账订单。 -->
|
||||
<select id="selectPrcOrderBillByCustomerPeriod" resultMap="PrcOrderBillResult">
|
||||
<include refid="selectPrcOrderBillVo"/>
|
||||
where customer_id = #{customerId}
|
||||
@@ -234,11 +234,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</if>
|
||||
<if test="orderOrTrackingNo != null and orderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
<if test="excludeOrderOrTrackingNo != null and excludeOrderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{excludeOrderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{excludeOrderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{excludeOrderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{excludeOrderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
and biz_date <= #{cutoffDate}
|
||||
and ar_bill_status = #{arBillStatus}
|
||||
@@ -263,11 +265,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</if>
|
||||
<if test="orderOrTrackingNo != null and orderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
<if test="excludeOrderOrTrackingNo != null and excludeOrderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{excludeOrderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{excludeOrderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{excludeOrderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{excludeOrderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
and biz_date <= #{cutoffDate}
|
||||
and ar_bill_status = #{arBillStatus}
|
||||
@@ -283,7 +287,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</if>
|
||||
<if test="orderOrTrackingNo != null and orderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
and biz_date <= #{cutoffDate}
|
||||
and ar_bill_status = #{arBillStatus}
|
||||
@@ -339,7 +344,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</if>
|
||||
<if test="orderOrTrackingNo != null and orderOrTrackingNo != ''">
|
||||
and (order_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
or tracking_no like concat('%', #{orderOrTrackingNo}, '%')
|
||||
or transfer_no like concat('%', #{orderOrTrackingNo}, '%'))
|
||||
</if>
|
||||
<if test="currency != null and currency != ''">
|
||||
and currency = #{currency}
|
||||
|
||||
@@ -76,6 +76,7 @@ CREATE TABLE IF NOT EXISTS ar_bill_detail (
|
||||
detail_no INT(11) NOT NULL COMMENT '账单内顺序号',
|
||||
order_no VARCHAR(64) NOT NULL COMMENT '订单号快照',
|
||||
tracking_no VARCHAR(64) DEFAULT NULL COMMENT '运单号快照',
|
||||
transfer_no VARCHAR(64) DEFAULT NULL COMMENT '转单号快照',
|
||||
receive_time DATETIME DEFAULT NULL COMMENT '收件或入库时间',
|
||||
product_code VARCHAR(50) NOT NULL COMMENT '产品代码快照',
|
||||
product_name VARCHAR(100) DEFAULT NULL COMMENT '产品名称快照',
|
||||
@@ -98,6 +99,7 @@ CREATE TABLE IF NOT EXISTS ar_bill_detail (
|
||||
UNIQUE KEY uk_ar_bill_detail_source (ar_bill_id, prc_order_bill_id),
|
||||
UNIQUE KEY uk_ar_bill_detail_no (ar_bill_id, detail_no),
|
||||
KEY idx_ar_bill_detail_source (prc_order_bill_id),
|
||||
KEY idx_ar_bill_detail_bill_transfer_no (ar_bill_id, transfer_no),
|
||||
CONSTRAINT fk_ar_bill_detail_bill FOREIGN KEY (ar_bill_id) REFERENCES ar_bill (ar_bill_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收账单订单快照';
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
-- 应收账单转单号快照迁移
|
||||
-- 适用于已经执行过 ar/ar_bill.sql 的存量数据库。
|
||||
-- 本脚本可重复执行,不删除账单明细,不覆盖已经存在的转单号快照。
|
||||
SET NAMES utf8mb4;
|
||||
SET @db_name := DATABASE();
|
||||
|
||||
-- 1. 账单明细保存生成账单时的转单号快照。
|
||||
SET @has_transfer_no := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db_name
|
||||
AND TABLE_NAME = 'ar_bill_detail'
|
||||
AND COLUMN_NAME = 'transfer_no'
|
||||
);
|
||||
SET @sql := IF(@has_transfer_no = 0,
|
||||
'ALTER TABLE ar_bill_detail ADD COLUMN transfer_no VARCHAR(64) DEFAULT NULL COMMENT ''转单号快照'' AFTER tracking_no',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 2. 详情按账单查询转单号时使用组合索引。
|
||||
SET @has_transfer_index := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db_name
|
||||
AND TABLE_NAME = 'ar_bill_detail'
|
||||
AND INDEX_NAME = 'idx_ar_bill_detail_bill_transfer_no'
|
||||
);
|
||||
SET @sql := IF(@has_transfer_index = 0,
|
||||
'ALTER TABLE ar_bill_detail ADD KEY idx_ar_bill_detail_bill_transfer_no (ar_bill_id, transfer_no)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 3. 历史数据尽力回填,仅填充空值,不覆盖账单生成时已经保存的快照。
|
||||
-- 如果来源订单后续修改过转单号,回填值仅代表当前来源表值,不能视为严格历史值。
|
||||
UPDATE ar_bill_detail d
|
||||
JOIN prc_order_bill p ON p.bill_id = d.prc_order_bill_id
|
||||
SET d.transfer_no = p.transfer_no
|
||||
WHERE d.transfer_no IS NULL
|
||||
AND p.transfer_no IS NOT NULL;
|
||||
|
||||
-- 4. 执行后核验字段和回填数量。
|
||||
SELECT
|
||||
COUNT(*) AS detail_count,
|
||||
SUM(CASE WHEN transfer_no IS NOT NULL AND TRIM(transfer_no) <> '' THEN 1 ELSE 0 END) AS transfer_no_count
|
||||
FROM ar_bill_detail;
|
||||
@@ -0,0 +1,99 @@
|
||||
-- ================================================================
|
||||
-- BMS 应收账单导出模板恢复脚本
|
||||
--
|
||||
-- 用途:恢复应收账单导出功能使用的内置默认模板配置。
|
||||
-- 特点:
|
||||
-- 1. 可重复执行,不删除现有模板数据。
|
||||
-- 2. 模板表不存在时自动创建;模板记录已存在时不会重复插入。
|
||||
-- 3. storage_key 必须与后端 resources 下的模板路径一致。
|
||||
-- ================================================================
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- 1. 创建导出模板表(表已存在时不会重复创建)
|
||||
CREATE TABLE IF NOT EXISTS ar_export_template (
|
||||
template_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '导出模板ID',
|
||||
template_code VARCHAR(50) NOT NULL COMMENT '模板编码,例如 AR_CUSTOMER_BILL',
|
||||
template_name VARCHAR(100) NOT NULL COMMENT '模板名称',
|
||||
business_type VARCHAR(50) NOT NULL COMMENT '业务类型,例如 AR_BILL',
|
||||
template_version VARCHAR(50) NOT NULL COMMENT '模板版本,已启用版本不可覆盖',
|
||||
storage_type VARCHAR(20) NOT NULL DEFAULT 'CLASSPATH' COMMENT '存储类型:CLASSPATH=应用内置资源,FILE=服务器文件,OSS=对象存储,MINIO=MinIO对象存储',
|
||||
storage_key VARCHAR(500) NOT NULL COMMENT '模板文件存储Key或classpath资源路径',
|
||||
file_hash VARCHAR(128) DEFAULT NULL COMMENT '模板文件SHA-256校验值',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED=启用,DISABLED=停用',
|
||||
is_default CHAR(1) NOT NULL DEFAULT '0' COMMENT '是否默认模板:1是0否',
|
||||
effective_start DATE DEFAULT NULL COMMENT '生效开始日期',
|
||||
effective_end DATE DEFAULT NULL COMMENT '生效结束日期',
|
||||
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
create_by VARCHAR(64) NOT NULL COMMENT '创建人',
|
||||
create_time DATETIME NOT NULL COMMENT '创建时间',
|
||||
update_by VARCHAR(64) DEFAULT NULL COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (template_id),
|
||||
UNIQUE KEY uk_ar_export_template_code_version (business_type, template_code, template_version),
|
||||
KEY idx_ar_export_template_default (business_type, status, is_default, effective_start, effective_end)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收账单导出模板管理';
|
||||
|
||||
-- 2. 恢复系统内置的应收账单默认模板
|
||||
-- 模板文件位置:
|
||||
-- bms-back/ruoyi-system/src/main/resources/templates/ar/ar_bill_template.xlsx
|
||||
INSERT INTO ar_export_template (
|
||||
template_code,
|
||||
template_name,
|
||||
business_type,
|
||||
template_version,
|
||||
storage_type,
|
||||
storage_key,
|
||||
file_hash,
|
||||
status,
|
||||
is_default,
|
||||
effective_start,
|
||||
effective_end,
|
||||
remark,
|
||||
create_by,
|
||||
create_time,
|
||||
update_by,
|
||||
update_time
|
||||
)
|
||||
SELECT
|
||||
'AR_CUSTOMER_BILL',
|
||||
'应收账单默认模板',
|
||||
'AR_BILL',
|
||||
'1.0',
|
||||
'CLASSPATH',
|
||||
'templates/ar/ar_bill_template.xlsx',
|
||||
'735002ac3561e0579b724c0612a2c5ce2d6725bc7fd39c450b0aeff70911a45e',
|
||||
'ENABLED',
|
||||
'1',
|
||||
NULL,
|
||||
NULL,
|
||||
'系统内置默认模板',
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM ar_export_template
|
||||
WHERE business_type = 'AR_BILL'
|
||||
AND template_code = 'AR_CUSTOMER_BILL'
|
||||
AND template_version = '1.0'
|
||||
);
|
||||
|
||||
-- 3. 执行后核验:应返回 1 条启用的默认模板记录
|
||||
SELECT
|
||||
template_id,
|
||||
template_code,
|
||||
template_name,
|
||||
business_type,
|
||||
template_version,
|
||||
storage_type,
|
||||
storage_key,
|
||||
status,
|
||||
is_default,
|
||||
file_hash
|
||||
FROM ar_export_template
|
||||
WHERE business_type = 'AR_BILL'
|
||||
AND template_code = 'AR_CUSTOMER_BILL'
|
||||
AND template_version = '1.0';
|
||||
Reference in New Issue
Block a user