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 + "]只能传入一个值");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -2,6 +2,8 @@ package com.ruoyi.web.controller.customer.customermoney;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -17,6 +19,7 @@ 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.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.customer.customermoney.api.ICustomerAccountApi;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerCreditApply;
|
||||
@@ -25,6 +28,9 @@ import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.service.ICreditQuotaService;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerAccountStatusVO;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerBalanceFlowExportVO;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerCreditFlowExportVO;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerReceiptExportVO;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
|
||||
/**
|
||||
@@ -89,6 +95,24 @@ public class CustomerMoneyController extends BaseController
|
||||
List<CustomerReceipt> list = customerMoneyService.selectReceiptList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出收款记录(按当前筛选条件,全量导出不分页)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
|
||||
@Log(title = "客户收款记录", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/receipt/export")
|
||||
public void receiptExport(HttpServletResponse response, CustomerReceipt query)
|
||||
{
|
||||
List<CustomerReceiptExportVO> list = Collections.emptyList();
|
||||
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
|
||||
{
|
||||
list = customerMoneyService.selectReceiptList(query).stream()
|
||||
.map(CustomerReceiptExportVO::from).collect(Collectors.toList());
|
||||
}
|
||||
ExcelUtil<CustomerReceiptExportVO> util = new ExcelUtil<>(CustomerReceiptExportVO.class);
|
||||
util.exportExcel(response, list, "收款记录");
|
||||
}
|
||||
/**
|
||||
* 获取收款单详情(含收款附件)
|
||||
*/
|
||||
@@ -149,6 +173,24 @@ public class CustomerMoneyController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出余额流水(按当前筛选条件,全量导出不分页)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
|
||||
@Log(title = "客户余额流水", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/flow/export")
|
||||
public void flowExport(HttpServletResponse response, CustomerBalanceFlow query)
|
||||
{
|
||||
List<CustomerBalanceFlowExportVO> list = Collections.emptyList();
|
||||
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
|
||||
{
|
||||
list = customerMoneyService.selectFlowList(query).stream()
|
||||
.map(CustomerBalanceFlowExportVO::from).collect(Collectors.toList());
|
||||
}
|
||||
ExcelUtil<CustomerBalanceFlowExportVO> util = new ExcelUtil<>(CustomerBalanceFlowExportVO.class);
|
||||
util.exportExcel(response, list, "余额流水");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询额度流水列表
|
||||
*/
|
||||
@@ -164,6 +206,24 @@ public class CustomerMoneyController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出额度流水(按当前筛选条件,全量导出不分页)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
|
||||
@Log(title = "客户额度流水", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/credit/flow/export")
|
||||
public void creditFlowExport(HttpServletResponse response, CustomerCreditFlow query)
|
||||
{
|
||||
List<CustomerCreditFlowExportVO> list = Collections.emptyList();
|
||||
if (query != null && query.getCustomerId() != null && query.getCustomerId() != 0L)
|
||||
{
|
||||
list = customerMoneyService.selectCreditFlowList(query).stream()
|
||||
.map(CustomerCreditFlowExportVO::from).collect(Collectors.toList());
|
||||
}
|
||||
ExcelUtil<CustomerCreditFlowExportVO> util = new ExcelUtil<>(CustomerCreditFlowExportVO.class);
|
||||
util.exportExcel(response, list, "额度流水");
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交额度调整申请(信用额度/临时额度),申请待审批通过后才真正调整额度
|
||||
*/
|
||||
|
||||
+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("客户和账单截止日不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
+44
-2
@@ -376,9 +376,10 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
BigDecimal afterBalance = beforeBalance.subtract(changeAmount);
|
||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||
account.setBalance(afterBalance);
|
||||
BigDecimal beforeUsedCredit = nz(account.getUsedCredit());
|
||||
if (Boolean.TRUE.equals(command.getReleaseUsedCredit()))
|
||||
{
|
||||
account.setUsedCredit(nz(account.getUsedCredit()).subtract(changeAmount).max(BigDecimal.ZERO)
|
||||
account.setUsedCredit(beforeUsedCredit.subtract(changeAmount).max(BigDecimal.ZERO)
|
||||
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
||||
}
|
||||
account.setUpdateBy(username);
|
||||
@@ -390,6 +391,18 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, -1,
|
||||
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
||||
customerMoneyMapper.insertFlow(flow);
|
||||
|
||||
// 核销扣款联动释放已用额度:补写额度流水(RELEASE),保证 used_credit 变动台账完整
|
||||
BigDecimal afterUsedCredit = nz(account.getUsedCredit());
|
||||
if (Boolean.TRUE.equals(command.getReleaseUsedCredit())
|
||||
&& afterUsedCredit.compareTo(beforeUsedCredit) < 0)
|
||||
{
|
||||
writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, null),
|
||||
CustomerCreditFlow.CHANGE_RELEASE, -1,
|
||||
beforeUsedCredit, afterUsedCredit,
|
||||
beforeUsedCredit.subtract(afterUsedCredit),
|
||||
username, userId);
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
@@ -412,10 +425,11 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
BigDecimal afterBalance = beforeBalance.add(changeAmount);
|
||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||
account.setBalance(afterBalance);
|
||||
BigDecimal beforeUsedCredit = nz(account.getUsedCredit());
|
||||
if (Boolean.TRUE.equals(command.getReoccupyUsedCredit()))
|
||||
{
|
||||
// 反核销回补:账单恢复未清偿,授信占用同步加回(允许出现高于总额度的占用存量)
|
||||
account.setUsedCredit(nz(account.getUsedCredit()).add(changeAmount)
|
||||
account.setUsedCredit(beforeUsedCredit.add(changeAmount)
|
||||
.setScale(MONEY_SCALE, RoundingMode.HALF_UP));
|
||||
}
|
||||
account.setUpdateBy(username);
|
||||
@@ -427,11 +441,39 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, 1,
|
||||
beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId);
|
||||
customerMoneyMapper.insertFlow(flow);
|
||||
|
||||
// 反核销回补联动重新占用已用额度:补写额度流水(OCCUPY),与核销释放流水方向相反、可勾稽
|
||||
BigDecimal afterUsedCredit = nz(account.getUsedCredit());
|
||||
if (Boolean.TRUE.equals(command.getReoccupyUsedCredit())
|
||||
&& afterUsedCredit.compareTo(beforeUsedCredit) > 0)
|
||||
{
|
||||
String reoccupyReason = "反核销重新占用授信额度"
|
||||
+ (StringUtils.hasText(command.getBizNo()) ? ":" + command.getBizNo() : "");
|
||||
writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, reoccupyReason),
|
||||
CustomerCreditFlow.CHANGE_OCCUPY, 1,
|
||||
beforeUsedCredit, afterUsedCredit,
|
||||
afterUsedCredit.subtract(beforeUsedCredit),
|
||||
username, userId);
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
/* ================= 内部实现 ================= */
|
||||
|
||||
/** 余额变动命令 -> 额度占用命令(复用业务单据信息,金额取实际联动额由调用方写流水时传入) */
|
||||
private CreditUsageCommand toCreditUsageCommand(BalanceChangeCommand source, String accountCurrency, String reason)
|
||||
{
|
||||
CreditUsageCommand target = new CreditUsageCommand();
|
||||
target.setCustomerId(source.getCustomerId());
|
||||
target.setCurrency(accountCurrency);
|
||||
target.setAmount(money(source.getAmount()));
|
||||
target.setBizType(source.getBizType());
|
||||
target.setBizId(source.getBizId());
|
||||
target.setBizNo(source.getBizNo());
|
||||
target.setReason(reason);
|
||||
return target;
|
||||
}
|
||||
|
||||
/** 额度占用/释放写额度流水(before/after 为已用额度快照) */
|
||||
private CustomerCreditFlow writeUsedCreditFlow(CustomerAccount account, CreditUsageCommand command,
|
||||
String changeType, int direction, BigDecimal beforeUsed, BigDecimal afterUsed,
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.ruoyi.customer.customermoney.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 客户余额流水导出 VO(列与页面余额流水 tab 对齐,枚举导出为中文文案)
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-21
|
||||
*/
|
||||
@Data
|
||||
public class CustomerBalanceFlowExportVO implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "流水号", width = 22)
|
||||
private String flowNo;
|
||||
|
||||
@Excel(name = "时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@Excel(name = "业务类型", width = 12)
|
||||
private String bizTypeLabel;
|
||||
|
||||
@Excel(name = "收支", width = 8)
|
||||
private String directionLabel;
|
||||
|
||||
@Excel(name = "原币金额", width = 16)
|
||||
private String amountText;
|
||||
|
||||
@Excel(name = "汇率", width = 12)
|
||||
private String exchangeRate;
|
||||
|
||||
@Excel(name = "本位币变动(CNY)", width = 18)
|
||||
private BigDecimal baseChangeAmount;
|
||||
|
||||
@Excel(name = "变动前余额", width = 14)
|
||||
private BigDecimal beforeBalance;
|
||||
|
||||
@Excel(name = "变动后余额", width = 14)
|
||||
private BigDecimal afterBalance;
|
||||
|
||||
@Excel(name = "关联单号", width = 22)
|
||||
private String bizNo;
|
||||
|
||||
@Excel(name = "操作人", width = 12)
|
||||
private String createBy;
|
||||
|
||||
@Excel(name = "备注", width = 30)
|
||||
private String remark;
|
||||
|
||||
public static CustomerBalanceFlowExportVO from(CustomerBalanceFlow f)
|
||||
{
|
||||
CustomerBalanceFlowExportVO vo = new CustomerBalanceFlowExportVO();
|
||||
vo.setFlowNo(f.getFlowNo());
|
||||
vo.setCreateTime(f.getCreateTime());
|
||||
vo.setBizTypeLabel(bizTypeLabel(f.getBizType()));
|
||||
boolean income = f.getDirection() != null && f.getDirection() == 1;
|
||||
vo.setDirectionLabel(income ? "收入" : "支出");
|
||||
String amt = f.getAmount() == null ? "" : f.getAmount().stripTrailingZeros().toPlainString();
|
||||
vo.setAmountText((f.getCurrency() == null ? "" : f.getCurrency() + " ") + amt);
|
||||
vo.setExchangeRate(f.getExchangeRate() == null ? null : f.getExchangeRate().stripTrailingZeros().toPlainString());
|
||||
BigDecimal base = f.getBaseAmount() != null ? f.getBaseAmount() : f.getAmount();
|
||||
// 支出导出为负数(配合“收支”列阅读,同时保持 Excel 数值类型)
|
||||
vo.setBaseChangeAmount(base == null ? null : (income ? base : base.negate()));
|
||||
vo.setBeforeBalance(f.getBeforeBalance());
|
||||
vo.setAfterBalance(f.getAfterBalance());
|
||||
vo.setBizNo(f.getBizNo());
|
||||
vo.setCreateBy(f.getCreateBy());
|
||||
vo.setRemark(f.getRemark());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static String bizTypeLabel(String v)
|
||||
{
|
||||
if ("RECEIPT".equals(v)) return "收款";
|
||||
if ("AR_BILL".equals(v)) return "应收账单";
|
||||
if ("BILL_PAY".equals(v)) return "账单核销";
|
||||
if ("ORDER".equals(v)) return "计费订单";
|
||||
if ("REFUND".equals(v)) return "退款";
|
||||
if ("ADJUST".equals(v)) return "手工调整";
|
||||
if ("FREEZE".equals(v)) return "冻结";
|
||||
if ("UNFREEZE".equals(v)) return "解冻";
|
||||
return v;
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package com.ruoyi.customer.customermoney.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 客户额度流水导出 VO(列与页面额度流水 tab 对齐,枚举导出为中文文案)
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-21
|
||||
*/
|
||||
@Data
|
||||
public class CustomerCreditFlowExportVO implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "流水号", width = 22)
|
||||
private String flowNo;
|
||||
|
||||
@Excel(name = "时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@Excel(name = "业务单号", width = 22)
|
||||
private String bizNo;
|
||||
|
||||
@Excel(name = "变动类型", width = 12)
|
||||
private String changeTypeLabel;
|
||||
|
||||
@Excel(name = "变动维度", width = 18)
|
||||
private String dimensionLabel;
|
||||
|
||||
@Excel(name = "调整方式", width = 10)
|
||||
private String adjustModeLabel;
|
||||
|
||||
@Excel(name = "方向", width = 8)
|
||||
private String directionLabel;
|
||||
|
||||
@Excel(name = "变动前", width = 12)
|
||||
private BigDecimal beforeAmount;
|
||||
|
||||
@Excel(name = "变动额", width = 12)
|
||||
private BigDecimal changeAmount;
|
||||
|
||||
@Excel(name = "变动后", width = 12)
|
||||
private BigDecimal afterAmount;
|
||||
|
||||
@Excel(name = "已用额度快照", width = 14)
|
||||
private BigDecimal usedCredit;
|
||||
|
||||
@Excel(name = "生效日期", width = 12, dateFormat = "yyyy-MM-dd")
|
||||
private Date effectiveDate;
|
||||
|
||||
@Excel(name = "失效日期", width = 12)
|
||||
private String expireDateText;
|
||||
|
||||
@Excel(name = "调整原因", width = 30)
|
||||
private String reason;
|
||||
|
||||
@Excel(name = "操作人", width = 12)
|
||||
private String createBy;
|
||||
|
||||
@Excel(name = "备注", width = 30)
|
||||
private String remark;
|
||||
|
||||
public static CustomerCreditFlowExportVO from(CustomerCreditFlow f)
|
||||
{
|
||||
CustomerCreditFlowExportVO vo = new CustomerCreditFlowExportVO();
|
||||
vo.setFlowNo(f.getFlowNo());
|
||||
vo.setCreateTime(f.getCreateTime());
|
||||
vo.setBizNo(f.getBizNo());
|
||||
vo.setChangeTypeLabel(changeTypeLabel(f.getChangeType()));
|
||||
boolean usageFlow = "OCCUPY".equals(f.getChangeType()) || "RELEASE".equals(f.getChangeType());
|
||||
vo.setDimensionLabel(usageFlow ? "授信占用(已用额度)" : "授信额度");
|
||||
vo.setAdjustModeLabel(adjustModeLabel(f.getAdjustMode()));
|
||||
Integer dir = f.getDirection();
|
||||
vo.setDirectionLabel(dir == null ? "-" : (dir == 1 ? "增加" : "减少"));
|
||||
vo.setBeforeAmount(f.getBeforeAmount());
|
||||
// 减少导出为负数(配合“方向”列阅读,同时保持 Excel 数值类型)
|
||||
BigDecimal change = f.getChangeAmount();
|
||||
if (change != null && dir != null && dir == -1)
|
||||
{
|
||||
change = change.negate();
|
||||
}
|
||||
vo.setChangeAmount(change);
|
||||
vo.setAfterAmount(f.getAfterAmount());
|
||||
// 占用/释放流水的已用额度快照在页面上不展示
|
||||
vo.setUsedCredit(usageFlow ? null : f.getUsedCredit());
|
||||
vo.setEffectiveDate(f.getEffectiveDate());
|
||||
vo.setExpireDateText(f.getExpireDate() == null ? "长期" : new java.text.SimpleDateFormat("yyyy-MM-dd").format(f.getExpireDate()));
|
||||
vo.setReason(f.getReason());
|
||||
vo.setCreateBy(f.getCreateBy());
|
||||
vo.setRemark(f.getRemark());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static String changeTypeLabel(String v)
|
||||
{
|
||||
if ("INIT".equals(v)) return "初始化";
|
||||
if ("ADJUST".equals(v)) return "手工调整";
|
||||
if ("EXPIRE".equals(v)) return "到期失效";
|
||||
if ("OCCUPY".equals(v)) return "授信占用";
|
||||
if ("RELEASE".equals(v)) return "授信释放";
|
||||
return v;
|
||||
}
|
||||
|
||||
private static String adjustModeLabel(String v)
|
||||
{
|
||||
if ("SET".equals(v)) return "设置为";
|
||||
if ("ADD".equals(v)) return "增加";
|
||||
if ("SUB".equals(v)) return "减少";
|
||||
return v;
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.ruoyi.customer.customermoney.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 客户收款记录导出 VO(列与页面收款记录 tab 对齐,枚举导出为中文文案)
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-21
|
||||
*/
|
||||
@Data
|
||||
public class CustomerReceiptExportVO implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "收款单号", width = 22)
|
||||
private String receiptNo;
|
||||
|
||||
@Excel(name = "收款日期", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date receivedAt;
|
||||
|
||||
@Excel(name = "收款类型", width = 12)
|
||||
private String receiptTypeLabel;
|
||||
|
||||
@Excel(name = "币种", width = 8)
|
||||
private String currency;
|
||||
|
||||
@Excel(name = "原币金额", width = 14)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Excel(name = "汇率", width = 12)
|
||||
private String exchangeRate;
|
||||
|
||||
@Excel(name = "汇率来源", width = 10)
|
||||
private String rateSourceLabel;
|
||||
|
||||
@Excel(name = "本位币金额(CNY)", width = 16)
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
@Excel(name = "收款方式", width = 12)
|
||||
private String payMethodLabel;
|
||||
|
||||
@Excel(name = "客户银行账号", width = 24)
|
||||
private String bankAccountNo;
|
||||
|
||||
@Excel(name = "银行/平台流水号", width = 22)
|
||||
private String bankTransactionNo;
|
||||
|
||||
@Excel(name = "状态", width = 10)
|
||||
private String statusLabel;
|
||||
|
||||
@Excel(name = "登记人", width = 12)
|
||||
private String createBy;
|
||||
|
||||
@Excel(name = "备注", width = 30)
|
||||
private String remark;
|
||||
|
||||
public static CustomerReceiptExportVO from(CustomerReceipt r)
|
||||
{
|
||||
CustomerReceiptExportVO vo = new CustomerReceiptExportVO();
|
||||
vo.setReceiptNo(r.getReceiptNo());
|
||||
vo.setReceivedAt(r.getReceivedAt());
|
||||
vo.setReceiptTypeLabel(receiptTypeLabel(r.getReceiptType()));
|
||||
vo.setCurrency(r.getCurrency());
|
||||
vo.setAmount(r.getAmount());
|
||||
vo.setExchangeRate(r.getExchangeRate() == null ? null : r.getExchangeRate().stripTrailingZeros().toPlainString());
|
||||
vo.setRateSourceLabel(rateSourceLabel(r.getRateSource()));
|
||||
vo.setBaseAmount(r.getBaseAmount());
|
||||
vo.setPayMethodLabel(payMethodLabel(r.getPayMethod()));
|
||||
vo.setBankAccountNo(r.getBankAccountNo());
|
||||
vo.setBankTransactionNo(r.getBankTransactionNo());
|
||||
vo.setStatusLabel(statusLabel(r.getStatus()));
|
||||
vo.setCreateBy(r.getCreateBy());
|
||||
vo.setRemark(r.getRemark());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static String receiptTypeLabel(String v)
|
||||
{
|
||||
if ("PREPAY".equals(v)) return "预充值";
|
||||
if ("REPAY".equals(v)) return "还款核销";
|
||||
if ("MIXED".equals(v)) return "混合";
|
||||
return v;
|
||||
}
|
||||
|
||||
private static String statusLabel(String v)
|
||||
{
|
||||
if ("PENDING".equals(v)) return "待确认";
|
||||
if ("CONFIRMED".equals(v)) return "已确认";
|
||||
if ("CANCELLED".equals(v)) return "已取消";
|
||||
return v;
|
||||
}
|
||||
|
||||
private static String payMethodLabel(String v)
|
||||
{
|
||||
if ("BANK_TRANSFER".equals(v)) return "银行转账";
|
||||
if ("ALIPAY".equals(v)) return "支付宝";
|
||||
if ("WECHAT".equals(v)) return "微信";
|
||||
if ("CASH".equals(v)) return "现金";
|
||||
if ("OTHER".equals(v)) return "其他";
|
||||
return v;
|
||||
}
|
||||
|
||||
private static String rateSourceLabel(String v)
|
||||
{
|
||||
if ("RATE_TABLE".equals(v)) return "汇率表";
|
||||
if ("MANUAL".equals(v)) return "手工修改";
|
||||
if ("SYSTEM".equals(v)) return "系统默认";
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -365,6 +365,10 @@ async function getList() {
|
||||
total.value = res?.total || 0
|
||||
} catch (e) {
|
||||
console.error('BasicTable 加载失败', e)
|
||||
// 查询失败时清空旧结果,避免用户误以为旧列表是本次查询结果。
|
||||
tableData.value = []
|
||||
total.value = 0
|
||||
selectionList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<el-popover :width="240" placement="bottom-end" trigger="click" popper-class="column-settings-popper">
|
||||
<template #reference>
|
||||
<el-button circle plain icon="Operation" title="列设置" />
|
||||
</template>
|
||||
<div class="column-settings">
|
||||
<div class="column-settings__header">
|
||||
<span class="column-settings__title">列展示设置</span>
|
||||
<el-button link type="primary" size="small" @click="emit('reset')">恢复默认</el-button>
|
||||
</div>
|
||||
<el-checkbox-group :model-value="checkedKeys" @change="onChange" class="column-settings__body">
|
||||
<el-checkbox
|
||||
v-for="c in columns"
|
||||
:key="c.key"
|
||||
:value="c.key"
|
||||
:disabled="c.fixed"
|
||||
>{{ c.label }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script setup name="ColumnSettings">
|
||||
/**
|
||||
* 表格列设置弹窗(与 useColumnSettings 配合):
|
||||
* <column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||
* 注意:reference 不要嵌套 el-tooltip——两个 popper 组件嵌套会导致引用解析异常。
|
||||
*/
|
||||
const props = defineProps({
|
||||
columns: { type: Array, default: () => [] },
|
||||
visibleMap: { type: Object, required: true }
|
||||
})
|
||||
const emit = defineEmits(['toggle', 'reset'])
|
||||
|
||||
const checkedKeys = computed(() =>
|
||||
props.columns.filter(c => props.visibleMap[c.key] !== false).map(c => c.key)
|
||||
)
|
||||
|
||||
/** el-checkbox-group 回传当前全量勾选值,逐列 diff 后通知父级修改 */
|
||||
function onChange(keys) {
|
||||
props.columns.forEach(c => {
|
||||
if (c.fixed) return
|
||||
const next = keys.includes(c.key)
|
||||
if ((props.visibleMap[c.key] !== false) !== next) {
|
||||
emit('toggle', c.key, next)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.column-settings__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
.column-settings__title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
}
|
||||
.column-settings__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.column-settings__body :deep(.el-checkbox) {
|
||||
margin-right: 0;
|
||||
height: 28px;
|
||||
}
|
||||
</style>
|
||||
@@ -57,6 +57,8 @@ import UserName from '@/components/UserName'
|
||||
import ProductSelect from '@/components/ProductSelect'
|
||||
// 多值输入组件(一个字段搜索多个值,如订单号批量搜索)
|
||||
import MultiValueInput from '@/components/MultiValueInput'
|
||||
// 表格列设置组件(自定义列显隐)
|
||||
import ColumnSettings from '@/components/ColumnSettings'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -87,6 +89,7 @@ app.component('UserSelect', UserSelect)
|
||||
app.component('UserName', UserName)
|
||||
app.component('ProductSelect', ProductSelect)
|
||||
app.component('MultiValueInput', MultiValueInput)
|
||||
app.component('ColumnSettings', ColumnSettings)
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
|
||||
@@ -200,6 +200,21 @@ export const dynamicRoutes = [
|
||||
meta: { title: '供应商编辑', activeMenu: '/customersuppliermanagement/supplierinfo' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// 兼容旧入口 /ar/bill/log;正式菜单路由由数据库菜单生成,当前菜单路径为 /ar/log。
|
||||
path: '/ar/bill/log',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['ar:log:list'],
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/ar/bill/log.vue'),
|
||||
name: 'ArBillLogCompat',
|
||||
meta: { title: '应收日志', activeMenu: '/ar/log' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 表格列显隐偏好(localStorage 持久化)。
|
||||
*
|
||||
* @param {string} storageKey 存储键(页面级唯一,如 'customer-list')
|
||||
* @param {Array<{key:string,label:string,fixed?:boolean,defaultVisible?:boolean}>} columns 列定义
|
||||
* @returns {{colVisible: object, isColVisible: Function, resetColumns: Function}}
|
||||
* colVisible 响应式显隐映射,模板中 v-if="isColVisible('xxx')"
|
||||
* resetColumns 恢复默认(供列设置弹窗调用)
|
||||
*/
|
||||
const PREFIX = 'bms:columnSettings:'
|
||||
|
||||
export function useColumnSettings(storageKey, columns) {
|
||||
const defaults = {}
|
||||
columns.forEach(c => {
|
||||
defaults[c.key] = c.defaultVisible !== false
|
||||
})
|
||||
|
||||
let saved = {}
|
||||
try {
|
||||
saved = JSON.parse(localStorage.getItem(PREFIX + storageKey) || '{}') || {}
|
||||
} catch (e) {
|
||||
saved = {}
|
||||
}
|
||||
|
||||
// 仅接受当前列定义中存在的 key(兼容历史存储与列增减)
|
||||
const colVisible = reactive({ ...defaults })
|
||||
columns.forEach(c => {
|
||||
if (Object.prototype.hasOwnProperty.call(saved, c.key)) {
|
||||
colVisible[c.key] = !!saved[c.key]
|
||||
}
|
||||
// 固定列强制可见
|
||||
if (c.fixed) colVisible[c.key] = true
|
||||
})
|
||||
|
||||
watch(colVisible, val => {
|
||||
localStorage.setItem(PREFIX + storageKey, JSON.stringify(val))
|
||||
}, { deep: true })
|
||||
|
||||
function isColVisible(key) {
|
||||
return colVisible[key] !== false
|
||||
}
|
||||
|
||||
/** 单列显隐切换(供 ColumnSettings 组件 @toggle 调用) */
|
||||
function toggleColumn(key, visible) {
|
||||
if (Object.prototype.hasOwnProperty.call(colVisible, key) || columns.some(c => c.key === key)) {
|
||||
colVisible[key] = !!visible
|
||||
}
|
||||
}
|
||||
|
||||
function resetColumns() {
|
||||
columns.forEach(c => {
|
||||
colVisible[c.key] = c.fixed ? true : c.defaultVisible !== false
|
||||
})
|
||||
}
|
||||
|
||||
return { colVisible, isColVisible, toggleColumn, resetColumns }
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer :model-value="modelValue" :title="detail?.billNo || '应收账单详情'" size="1000px" append-to-body destroy-on-close @update:model-value="$emit('update:modelValue', $event)">
|
||||
<el-drawer class="bill-detail-drawer" :model-value="modelValue" :title="detail?.billNo || '应收账单详情'" size="min(1000px, 100vw)" append-to-body destroy-on-close @update:model-value="$emit('update:modelValue', $event)">
|
||||
<div v-loading="loading">
|
||||
<el-empty v-if="!detail" description="暂无详情" />
|
||||
<template v-else>
|
||||
@@ -37,6 +37,7 @@
|
||||
<el-table-column prop="detailNo" label="#" width="55" align="center" />
|
||||
<el-table-column prop="orderNo" label="订单号" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="trackingNo" label="运单号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="transferNo" label="转单号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="产品" min-width="150"><template #default="{ row }">{{ row.productName || row.productCode || '-' }}</template></el-table-column>
|
||||
<el-table-column label="目的地" min-width="120"><template #default="{ row }">{{ row.countryName || row.countryCode || '-' }}</template></el-table-column>
|
||||
<el-table-column label="重量/尺寸" min-width="170"><template #default="{ row }">{{ row.chargeWeight || '-' }} kg / {{ dimensions(row) }}</template></el-table-column>
|
||||
@@ -139,6 +140,7 @@ function mask(value) { if (!value) return '-'; return value.length <= 4 ? '****'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.bill-detail-drawer) { max-width: 100vw; }
|
||||
.summary { margin-bottom: 18px; }
|
||||
.total { color: #f56c6c; font-size: 16px; font-weight: 600; }
|
||||
.section-title { display: flex; align-items: center; gap: 12px; margin: 18px 0 10px; font-weight: 600; }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<el-col :span="8"><el-form-item label="客户" prop="customerId"><customer-select v-model="generateForm.customerId" placeholder="请选择客户" @change="handleCustomerChange" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="币种" prop="currency"><el-select v-model="generateForm.currency" placeholder="请选择币种" style="width: 100%;" @change="handleCurrencyChange"><el-option label="人民币 CNY" value="CNY" /><el-option label="美元 USD" value="USD" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="账单截止日" prop="cutoffDate"><el-date-picker v-model="generateForm.cutoffDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择账单截止日" style="width: 100%;" @change="resetPreview" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="订单/运单号"><el-input v-model="generateForm.orderOrTrackingNo" placeholder="请输入订单号或运单号" clearable /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="单号"><el-input v-model="generateForm.orderOrTrackingNo" placeholder="请输入订单号、运单号或转单号" clearable /></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="payee-card" v-loading="bankAccountLoading">
|
||||
@@ -60,6 +60,7 @@
|
||||
<el-table-column type="selection" width="50" fixed="left" :selectable="isMainRowSelectable" />
|
||||
<el-table-column prop="orderNo" label="订单号" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="trackingNo" label="运单号" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="transferNo" label="转单号" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="customerName" label="客户" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="bizDate" label="业务日期" width="105" :formatter="formatDateColumn" />
|
||||
<el-table-column prop="productName" label="产品" min-width="140" show-overflow-tooltip />
|
||||
@@ -87,13 +88,14 @@
|
||||
|
||||
<el-dialog v-model="excludeDialogOpen" title="选择排除订单" width="1100px" append-to-body destroy-on-close @close="handleExcludeDialogClose">
|
||||
<div class="exclude-toolbar">
|
||||
<el-input v-model="excludeKeyword" clearable placeholder="请输入订单号或运单号" class="exclude-search-input" />
|
||||
<el-input v-model="excludeKeyword" clearable placeholder="请输入订单号、运单号或转单号" class="exclude-search-input" />
|
||||
<el-button type="primary" :loading="excludeLoading" @click="queryExcludeRows">查询</el-button>
|
||||
</div>
|
||||
<el-table ref="excludeTableRef" v-loading="excludeLoading" :data="excludeRowsPage" row-key="billId" border stripe max-height="420" empty-text="暂无符合条件的订单" @selection-change="handleExcludeSelectionChange">
|
||||
<el-table-column type="selection" width="50" fixed="left" />
|
||||
<el-table-column prop="orderNo" label="订单号" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="trackingNo" label="运单号" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="transferNo" label="转单号" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="customerName" label="客户" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="bizDate" label="业务日期" width="120" :formatter="formatDateColumn" />
|
||||
<el-table-column prop="incomeTotal" label="应收金额" width="130" align="right" :formatter="formatNumberColumn" />
|
||||
|
||||
+81
-16
@@ -110,6 +110,8 @@
|
||||
<el-button icon="Refresh" @click="resetReceiptQuery">重置</el-button>
|
||||
<el-button type="success" icon="Plus" @click="handleAddReceipt"
|
||||
v-hasPermi="['customersupplier:customerinfo:add']">登记收款</el-button>
|
||||
<el-button type="warning" icon="Download" @click="handleExportReceipt"
|
||||
v-hasPermi="['customersupplier:customerinfo:export']">导出</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -220,6 +222,8 @@
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="getFlowList">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetFlowQuery">重置</el-button>
|
||||
<el-button type="warning" icon="Download" @click="handleExportFlow"
|
||||
v-hasPermi="['customersupplier:customerinfo:export']">导出</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -296,6 +300,8 @@
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="getCreditFlowList">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetCreditFlowQuery">重置</el-button>
|
||||
<el-button type="warning" icon="Download" @click="handleExportCreditFlow"
|
||||
v-hasPermi="['customersupplier:customerinfo:export']">导出</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -918,7 +924,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 余额核销账单弹窗 ====== -->
|
||||
<el-dialog title="余额核销账单" v-model="woDialog.visible" width="560px" append-to-body>
|
||||
<el-dialog title="余额核销账单" v-model="woDialog.visible" width="560px" append-to-body v-loading="woDialog.accountLoading">
|
||||
<el-descriptions :column="1" border size="small" v-if="woDialog.bill">
|
||||
<el-descriptions-item label="账单号">{{ woDialog.bill.billNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单名称">{{ woDialog.bill.billName || '-' }}</el-descriptions-item>
|
||||
@@ -931,13 +937,12 @@
|
||||
</el-descriptions>
|
||||
<el-form :model="woDialog.form" label-width="110px" class="wo-form">
|
||||
<el-form-item label="本次核销金额" required>
|
||||
<el-input-number
|
||||
<el-input
|
||||
v-model="woDialog.form.writeoffAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0.01"
|
||||
controls-position="right"
|
||||
inputmode="decimal"
|
||||
clearable
|
||||
style="width: 220px;"
|
||||
placeholder="请输入核销金额"
|
||||
/>
|
||||
<span class="wo-form-unit">CNY</span>
|
||||
</el-form-item>
|
||||
@@ -1356,6 +1361,39 @@ function resetReceiptQuery() {
|
||||
getReceiptList()
|
||||
}
|
||||
|
||||
/** 导出收款记录(按当前筛选,全量导出) */
|
||||
function handleExportReceipt() {
|
||||
const cid = route.query.customerId
|
||||
if (!cid) return
|
||||
const params = proxy.addDateRange({ ...receiptQuery }, receiptDateRange.value)
|
||||
delete params.pageNum
|
||||
delete params.pageSize
|
||||
proxy.download('customersupplier/customermoney/receipt/export', params,
|
||||
`收款记录_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
/** 导出余额流水(按当前筛选,全量导出) */
|
||||
function handleExportFlow() {
|
||||
const cid = route.query.customerId
|
||||
if (!cid) return
|
||||
const params = proxy.addDateRange({ ...flowQuery }, flowDateRange.value)
|
||||
delete params.pageNum
|
||||
delete params.pageSize
|
||||
proxy.download('customersupplier/customermoney/flow/export', params,
|
||||
`余额流水_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
/** 导出额度流水(按当前筛选,全量导出) */
|
||||
function handleExportCreditFlow() {
|
||||
const cid = route.query.customerId
|
||||
if (!cid) return
|
||||
const params = proxy.addDateRange({ ...creditFlowQuery }, creditFlowDateRange.value)
|
||||
delete params.pageNum
|
||||
delete params.pageSize
|
||||
proxy.download('customersupplier/customermoney/credit/flow/export', params,
|
||||
`额度流水_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
/** 审核通过 */
|
||||
function handleConfirmReceipt(row) {
|
||||
proxy.$modal.confirm(`是否确认审核通过收款单「${row.receiptNo}」?审核通过后金额将计入客户余额。`).then(() => {
|
||||
@@ -1812,7 +1850,8 @@ function loadBillWriteoffData() {
|
||||
|
||||
/* ---- 核销弹窗 ---- */
|
||||
const woSubmitLoading = ref(false)
|
||||
const woDialog = reactive({ visible: false, bill: null, form: {} })
|
||||
const woDialog = reactive({ visible: false, bill: null, form: {}, accountLoading: false })
|
||||
let writeoffAccountRequestSerial = 0
|
||||
/** 核销后余额预览(不为负则可提交) */
|
||||
const woAfterBalance = computed(() => {
|
||||
const balance = Number(currentAccount.value.balance) || 0
|
||||
@@ -1820,27 +1859,53 @@ const woAfterBalance = computed(() => {
|
||||
return formatMoney(balance - amount)
|
||||
})
|
||||
|
||||
function openWriteoffDialog(row) {
|
||||
// 先刷新账户余额,避免用过期余额核销
|
||||
listAccount(route.query.customerId).then(res => {
|
||||
accountList.value = res.data || []
|
||||
})
|
||||
async function openWriteoffDialog(row) {
|
||||
const customerId = Number(route.query.customerId)
|
||||
if (!customerId) {
|
||||
proxy.$modal.msgError('缺少客户ID,无法发起核销')
|
||||
return
|
||||
}
|
||||
|
||||
woDialog.bill = row
|
||||
const balance = Number(currentAccount.value.balance) || 0
|
||||
const unwritten = Number(row.unwrittenAmount) || 0
|
||||
woDialog.form = {
|
||||
arBillId: row.arBillId,
|
||||
customerId: Number(route.query.customerId),
|
||||
writeoffAmount: Math.min(unwritten, balance > 0 ? balance : unwritten),
|
||||
customerId,
|
||||
// 余额接口返回前先显示 0,避免使用旧余额或在余额为 0 时错误带出整笔未核销金额。
|
||||
writeoffAmount: 0,
|
||||
writeoffDate: proxy.parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
remark: ''
|
||||
}
|
||||
woDialog.visible = true
|
||||
|
||||
// 必须等待余额刷新完成后再计算默认金额,避免异步请求尚未返回就读取旧余额。
|
||||
const requestSerial = ++writeoffAccountRequestSerial
|
||||
woDialog.accountLoading = true
|
||||
try {
|
||||
const res = await listAccount(customerId)
|
||||
if (requestSerial !== writeoffAccountRequestSerial) return
|
||||
accountList.value = res.data || []
|
||||
const balance = Math.max(0, Number(currentAccount.value.balance) || 0)
|
||||
woDialog.form.writeoffAmount = Math.min(unwritten, balance)
|
||||
} catch (e) {
|
||||
if (requestSerial !== writeoffAccountRequestSerial) return
|
||||
woDialog.form.writeoffAmount = 0
|
||||
proxy.$modal.msgError('账户余额刷新失败,请稍后重试')
|
||||
} finally {
|
||||
if (requestSerial === writeoffAccountRequestSerial) woDialog.accountLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
function submitWriteoff() {
|
||||
if (woDialog.accountLoading) {
|
||||
proxy.$modal.msgWarning('正在刷新账户余额,请稍候')
|
||||
return
|
||||
}
|
||||
const bill = woDialog.bill
|
||||
const amount = Number(woDialog.form.writeoffAmount)
|
||||
const rawAmount = typeof woDialog.form.writeoffAmount === 'string'
|
||||
? woDialog.form.writeoffAmount.trim()
|
||||
: woDialog.form.writeoffAmount
|
||||
const amount = Number(rawAmount)
|
||||
if (!(amount > 0)) {
|
||||
proxy.$modal.msgError('核销金额必须大于0')
|
||||
return
|
||||
|
||||
@@ -115,49 +115,50 @@
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
<column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table v-loading="loading" border :data="customerList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<!-- <el-table-column label="客户ID" align="center" prop="customerId" />-->
|
||||
<el-table-column label="客户编码" align="center" prop="customerCode" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="对外代码" align="center" prop="customerCodeDw" />
|
||||
<el-table-column label="统一社会信用代码" align="center" prop="creditCode" />
|
||||
<el-table-column label="业务类型" align="center" prop="customerType">
|
||||
<el-table-column v-if="isColVisible('customerCode')" label="客户编码" align="center" prop="customerCode" />
|
||||
<el-table-column v-if="isColVisible('customerName')" label="客户名称" align="center" prop="customerName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="isColVisible('customerCodeDw')" label="对外代码" align="center" prop="customerCodeDw" />
|
||||
<el-table-column v-if="isColVisible('creditCode')" label="统一社会信用代码" align="center" prop="creditCode" />
|
||||
<el-table-column v-if="isColVisible('customerType')" label="业务类型" align="center" prop="customerType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="customer_type" :value="scope.row.customerType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" align="center" prop="isEnable">
|
||||
<el-table-column v-if="isColVisible('isEnable')" label="启用状态" align="center" prop="isEnable">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.isEnable" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务员" align="center" prop="salesUserId">
|
||||
<el-table-column v-if="isColVisible('salesUserId')" label="业务员" align="center" prop="salesUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.salesUserId" /></template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="资金额度" align="center" prop="ed" />
|
||||
<el-table-column label="回款账期" align="center" prop="zq" />
|
||||
<el-table-column label="结算员" align="center" prop="settlementUserId">
|
||||
<el-table-column v-if="isColVisible('ed')" label="资金额度" align="center" prop="ed" />
|
||||
<el-table-column v-if="isColVisible('zq')" label="回款账期" align="center" prop="zq" />
|
||||
<el-table-column v-if="isColVisible('settlementUserId')" label="结算员" align="center" prop="settlementUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.settlementUserId" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客服" align="center" prop="serviceUserId">
|
||||
<el-table-column v-if="isColVisible('serviceUserId')" label="客服" align="center" prop="serviceUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.serviceUserId" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结算方式" align="center" prop="settlementType">
|
||||
<el-table-column v-if="isColVisible('settlementType')" label="结算方式" align="center" prop="settlementType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="settlement_type" :value="scope.row.settlementType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column label="账户余额(CNY)" align="center" prop="accountBalance" width="130">
|
||||
<el-table-column v-if="isColVisible('tempEd')" label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column v-if="isColVisible('accountBalance')" label="账户余额(CNY)" align="center" prop="accountBalance" width="130">
|
||||
<template #default="scope">{{ formatBalance(scope.row.accountBalance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column label="信用额度" align="center" prop="creditEd">
|
||||
<el-table-column v-if="isColVisible('weightThreshold')" label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column v-if="isColVisible('throwRatio')" label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column v-if="isColVisible('creditEd')" label="信用额度" align="center" prop="creditEd">
|
||||
<template #default="scope">
|
||||
<span v-if="Number(scope.row.creditEd) < 0" style="color: var(--el-color-danger); font-weight: 600;"
|
||||
title="已用额度超过授信总额(如临时额度已到期),客户回款核销后恢复">
|
||||
@@ -166,7 +167,7 @@
|
||||
<span v-else>{{ scope.row.creditEd }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="绑定产品" align="center" prop="productIds" min-width="180">
|
||||
<el-table-column v-if="isColVisible('productIds')" label="绑定产品" align="center" prop="productIds" min-width="180">
|
||||
<template #default="scope">
|
||||
<span v-if="productNames(scope.row.productIds)" :title="productNames(scope.row.productIds)">
|
||||
{{ productNames(scope.row.productIds) }}
|
||||
@@ -174,8 +175,8 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注说明" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
|
||||
<el-table-column v-if="isColVisible('remark')" label="备注说明" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['customersupplier:customerinfo:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:customerinfo:remove']">删除</el-button>
|
||||
@@ -198,9 +199,34 @@
|
||||
<script setup name="Customer">
|
||||
import { listCustomer, delCustomer } from "@/api/customersuppliermanagement/customerinfo/customer.js"
|
||||
import { useProducts, parseProductIds } from '@/components/ProductSelect/useProducts'
|
||||
import { useColumnSettings } from '@/utils/columnSettings'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
/** 可自定义列(默认全部可见;选择列固定、操作列固定右侧不参与设置) */
|
||||
const columnDefs = [
|
||||
{ key: 'customerCode', label: '客户编码' },
|
||||
{ key: 'customerName', label: '客户名称' },
|
||||
{ key: 'customerCodeDw', label: '对外代码' },
|
||||
{ key: 'creditCode', label: '统一社会信用代码' },
|
||||
{ key: 'customerType', label: '业务类型' },
|
||||
{ key: 'isEnable', label: '启用状态' },
|
||||
{ key: 'salesUserId', label: '业务员' },
|
||||
{ key: 'ed', label: '资金额度' },
|
||||
{ key: 'zq', label: '回款账期' },
|
||||
{ key: 'settlementUserId', label: '结算员' },
|
||||
{ key: 'serviceUserId', label: '客服' },
|
||||
{ key: 'settlementType', label: '结算方式' },
|
||||
{ key: 'tempEd', label: '临时额度' },
|
||||
{ key: 'accountBalance', label: '账户余额(CNY)' },
|
||||
{ key: 'weightThreshold', label: '重量阈值' },
|
||||
{ key: 'throwRatio', label: '抛比' },
|
||||
{ key: 'creditEd', label: '信用额度' },
|
||||
{ key: 'productIds', label: '绑定产品' },
|
||||
{ key: 'remark', label: '备注说明' }
|
||||
]
|
||||
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('customer-info-list', columnDefs)
|
||||
|
||||
const customerList = ref([])
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
|
||||
@@ -96,53 +96,54 @@
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
<column-settings :columns="columnDefs" :visible-map="colVisible" @toggle="toggleColumn" @reset="resetColumns" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="supplierList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="供应商编码" align="center" prop="supplierCode" />
|
||||
<el-table-column label="供应商名称" align="center" prop="supplierName" />
|
||||
<el-table-column label="对外代码" align="center" prop="supplierCodeDw" />
|
||||
<el-table-column label="统一社会信用代码" align="center" prop="creditCode" />
|
||||
<el-table-column label="供应商类型" align="center" prop="supplierType">
|
||||
<el-table v-loading="loading" border :data="supplierList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<el-table-column v-if="isColVisible('supplierCode')" label="供应商编码" align="center" prop="supplierCode" />
|
||||
<el-table-column v-if="isColVisible('supplierName')" label="供应商名称" align="center" prop="supplierName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="isColVisible('supplierCodeDw')" label="对外代码" align="center" prop="supplierCodeDw" />
|
||||
<el-table-column v-if="isColVisible('creditCode')" label="统一社会信用代码" align="center" prop="creditCode" />
|
||||
<el-table-column v-if="isColVisible('supplierType')" label="供应商类型" align="center" prop="supplierType">
|
||||
<template #default="scope">{{ formatOption(supplierTypeOptions, scope.row.supplierType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" align="center" prop="isEnable">
|
||||
<el-table-column v-if="isColVisible('isEnable')" label="启用状态" align="center" prop="isEnable">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.isEnable" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="采购员" align="center" prop="purchaserUserId">
|
||||
<el-table-column v-if="isColVisible('purchaserUserId')" label="采购员" align="center" prop="purchaserUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.purchaserUserId" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应付额度" align="center" prop="ed" />
|
||||
<el-table-column label="应付账期" align="center" prop="zq" />
|
||||
<el-table-column label="结算员" align="center" prop="settlementUserId">
|
||||
<el-table-column v-if="isColVisible('ed')" label="应付额度" align="center" prop="ed" />
|
||||
<el-table-column v-if="isColVisible('zq')" label="应付账期" align="center" prop="zq" />
|
||||
<el-table-column v-if="isColVisible('settlementUserId')" label="结算员" align="center" prop="settlementUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.settlementUserId" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="跟单员" align="center" prop="serviceUserId">
|
||||
<el-table-column v-if="isColVisible('serviceUserId')" label="跟单员" align="center" prop="serviceUserId">
|
||||
<template #default="scope"><user-name :user-id="scope.row.serviceUserId" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结算方式" align="center" prop="settlementType">
|
||||
<el-table-column v-if="isColVisible('settlementType')" label="结算方式" align="center" prop="settlementType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="settlement_type" :value="scope.row.settlementType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column label="应付余额" align="center" prop="accountBalance" />
|
||||
<el-table-column label="外币应付余额" align="center" prop="foreignCurrencyBalance" />
|
||||
<el-table-column label="信用额度" align="center" prop="creditEd" />
|
||||
<el-table-column label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column label="合作开始日期" align="center" prop="cooperationStartDate">
|
||||
<el-table-column v-if="isColVisible('tempEd')" label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column v-if="isColVisible('accountBalance')" label="应付余额" align="center" prop="accountBalance" />
|
||||
<el-table-column v-if="isColVisible('foreignCurrencyBalance')" label="外币应付余额" align="center" prop="foreignCurrencyBalance" />
|
||||
<el-table-column v-if="isColVisible('creditEd')" label="信用额度" align="center" prop="creditEd" />
|
||||
<el-table-column v-if="isColVisible('weightThreshold')" label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column v-if="isColVisible('throwRatio')" label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column v-if="isColVisible('cooperationStartDate')" label="合作开始日期" align="center" prop="cooperationStartDate">
|
||||
<template #default="scope">{{ formatDateOnly(scope.row.cooperationStartDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合作状态" align="center" prop="cooperationStatus">
|
||||
<el-table-column v-if="isColVisible('cooperationStatus')" label="合作状态" align="center" prop="cooperationStatus">
|
||||
<template #default="scope">{{ formatOption(cooperationStatusOptions, scope.row.cooperationStatus) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="绑定渠道ID" align="center" prop="channelIds" />
|
||||
<el-table-column label="备注说明" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
|
||||
<el-table-column v-if="isColVisible('channelIds')" label="绑定渠道ID" align="center" prop="channelIds" />
|
||||
<el-table-column v-if="isColVisible('remark')" label="备注说明" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['customersupplier:supplierinfo:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:supplierinfo:remove']">删除</el-button>
|
||||
@@ -162,9 +163,37 @@
|
||||
|
||||
<script setup name="Supplier">
|
||||
import { listSupplier, delSupplier } from "@/api/customersuppliermanagement/supplierinfo/supplier.js"
|
||||
import { useColumnSettings } from '@/utils/columnSettings'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
/** 可自定义列(默认全部可见;选择列固定、操作列固定右侧不参与设置) */
|
||||
const columnDefs = [
|
||||
{ key: 'supplierCode', label: '供应商编码' },
|
||||
{ key: 'supplierName', label: '供应商名称' },
|
||||
{ key: 'supplierCodeDw', label: '对外代码' },
|
||||
{ key: 'creditCode', label: '统一社会信用代码' },
|
||||
{ key: 'supplierType', label: '供应商类型' },
|
||||
{ key: 'isEnable', label: '启用状态' },
|
||||
{ key: 'purchaserUserId', label: '采购员' },
|
||||
{ key: 'ed', label: '应付额度' },
|
||||
{ key: 'zq', label: '应付账期' },
|
||||
{ key: 'settlementUserId', label: '结算员' },
|
||||
{ key: 'serviceUserId', label: '跟单员' },
|
||||
{ key: 'settlementType', label: '结算方式' },
|
||||
{ key: 'tempEd', label: '临时额度' },
|
||||
{ key: 'accountBalance', label: '应付余额' },
|
||||
{ key: 'foreignCurrencyBalance', label: '外币应付余额' },
|
||||
{ key: 'creditEd', label: '信用额度' },
|
||||
{ key: 'weightThreshold', label: '重量阈值' },
|
||||
{ key: 'throwRatio', label: '抛比' },
|
||||
{ key: 'cooperationStartDate', label: '合作开始日期' },
|
||||
{ key: 'cooperationStatus', label: '合作状态' },
|
||||
{ key: 'channelIds', label: '绑定渠道ID' },
|
||||
{ key: 'remark', label: '备注说明' }
|
||||
]
|
||||
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('supplier-info-list', columnDefs)
|
||||
|
||||
const supplierList = ref([])
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
|
||||
@@ -69,16 +69,16 @@ const schemas = computed(() => [
|
||||
width: '100%',
|
||||
props: { maxlength: 50 }
|
||||
},
|
||||
// {
|
||||
// label: '税模式',
|
||||
// prop: 'taxMode',
|
||||
// component: 'select',
|
||||
// placeholder: '请选择',
|
||||
// dict: 'bms_tax_mode',
|
||||
// span: 12,
|
||||
// width: '100%',
|
||||
// rules: [{ required: true, message: '请选择税模式', trigger: 'change' }]
|
||||
// },
|
||||
{
|
||||
label: '税模式',
|
||||
prop: 'taxMode',
|
||||
component: 'select',
|
||||
placeholder: '请选择',
|
||||
dict: 'bms_tax_mode',
|
||||
span: 12,
|
||||
width: '100%',
|
||||
rules: [{ required: true, message: '请选择税模式', trigger: 'change' }]
|
||||
},
|
||||
{
|
||||
label: '生效日期',
|
||||
prop: 'effectiveDate',
|
||||
|
||||
@@ -113,7 +113,7 @@ const columns = [
|
||||
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
|
||||
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
|
||||
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
|
||||
// { prop: 'taxMode', label: '税模式', width: 90, align: 'center', dict: 'bms_tax_mode' },
|
||||
{ prop: 'taxMode', label: '税模式', width: 90, align: 'center', dict: 'bms_tax_mode' },
|
||||
{ prop: 'effectiveDate', label: '生效日期', width: 120, align: 'center' },
|
||||
{ prop: 'expireDate', label: '失效日期', width: 120, align: 'center' },
|
||||
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
|
||||
@@ -125,15 +125,15 @@ const columns = [
|
||||
|
||||
const searchColumns = [
|
||||
{ prop: 'productCode', label: '产品代码', component: 'input', placeholder: '产品代码' },
|
||||
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' }
|
||||
// {
|
||||
// prop: 'taxMode',
|
||||
// label: '税模式',
|
||||
// component: 'select',
|
||||
// dict: 'bms_tax_mode',
|
||||
// placeholder: '税模式',
|
||||
// clearable: true
|
||||
// }
|
||||
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' },
|
||||
{
|
||||
prop: 'taxMode',
|
||||
label: '税模式',
|
||||
component: 'select',
|
||||
dict: 'bms_tax_mode',
|
||||
placeholder: '税模式',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
function queryList(params) {
|
||||
|
||||
Reference in New Issue
Block a user