优化计费逻辑
This commit is contained in:
+5
-5
@@ -131,7 +131,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(tabOrderMonitorService.selectOrderIdsByProduct(query));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "order source sync filter", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/syncByFilter")
|
||||
public AjaxResult syncByFilter(@RequestBody TabOrderMonitorQuery query)
|
||||
@@ -157,7 +157,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@GetMapping("/syncTask/{asyncTaskId}")
|
||||
public AjaxResult syncTaskProgress(@PathVariable("asyncTaskId") String asyncTaskId)
|
||||
{
|
||||
@@ -169,7 +169,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(task);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:sync') or @ss.hasPermi('price:orderBill:rebill')")
|
||||
@Log(title = "order source sync selected", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/sync")
|
||||
public AjaxResult syncSelected(@RequestBody List<TabOrderMonitorVo> rows)
|
||||
@@ -193,7 +193,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/syncLog/list")
|
||||
public TableDataInfo syncLogList(
|
||||
@RequestParam(value = "bizId", required = false) String bizId,
|
||||
@@ -211,7 +211,7 @@ public class TabOrderMonitorController extends BaseController
|
||||
return rsp;
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/syncLog/{docId}")
|
||||
public AjaxResult syncLogInfo(@PathVariable("docId") String docId)
|
||||
{
|
||||
|
||||
+45
-4
@@ -1,6 +1,8 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -127,29 +129,68 @@ public class BillingController extends BaseController
|
||||
@GetMapping("/bill/list")
|
||||
public TableDataInfo billList(PrcOrderBill query)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
query = new PrcOrderBill();
|
||||
}
|
||||
// Billing list only shows success and the three soft misses.
|
||||
query.setBillingFailStatuses(limitBillingListFailStatus(query.getBillingFailStatuses()));
|
||||
query.setBillingFailStatus(null);
|
||||
startPage();
|
||||
List<PrcOrderBill> list = prcOrderBillService.selectBillList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
private static List<Integer> limitBillingListFailStatus(List<Integer> requested)
|
||||
{
|
||||
List<Integer> allowed = Arrays.asList(Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
|
||||
if (requested == null || requested.isEmpty())
|
||||
{
|
||||
return new ArrayList<Integer>(allowed);
|
||||
}
|
||||
List<Integer> kept = new ArrayList<Integer>();
|
||||
for (Integer status : requested)
|
||||
{
|
||||
if (status != null && allowed.contains(status) && !kept.contains(status))
|
||||
{
|
||||
kept.add(status);
|
||||
}
|
||||
}
|
||||
if (kept.isEmpty())
|
||||
{
|
||||
kept.add(Integer.valueOf(-1));
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
/** All prc_order_bill rows. Billing list stays limited to status 0-3. */
|
||||
@PreAuthorize("@ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/orderBill/list")
|
||||
public TableDataInfo orderBillList(PrcOrderBill query)
|
||||
{
|
||||
startPage();
|
||||
List<PrcOrderBill> list = prcOrderBillService.selectBillList(query == null ? new PrcOrderBill() : query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Order no options for remote-search dropdown (bill list multi search).
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/orderNos")
|
||||
public AjaxResult orderNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
|
||||
{
|
||||
return success(prcOrderBillService.listOrderNoOptions(buildBillOptionQuery(keyword)));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/trackingNos")
|
||||
public AjaxResult trackingNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
|
||||
{
|
||||
return success(prcOrderBillService.listTrackingNoOptions(buildBillOptionQuery(keyword)));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/transferNos")
|
||||
public AjaxResult transferNoOptions(@RequestParam(value = "keyword", required = false) String keyword)
|
||||
{
|
||||
@@ -167,7 +208,7 @@ public class BillingController extends BaseController
|
||||
return query;
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill')")
|
||||
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
|
||||
@GetMapping("/bill/{billId}")
|
||||
public AjaxResult getBill(@PathVariable String billId)
|
||||
{
|
||||
|
||||
@@ -1604,10 +1604,29 @@ public class ArBillServiceImpl implements IArBillService {
|
||||
return "";
|
||||
}
|
||||
return switch (status) {
|
||||
case SUCCESS -> "";
|
||||
case PRODUCT -> "未匹配产品";
|
||||
case COUNTRY -> "未匹配国家";
|
||||
case WEIGHT -> "未匹配重量段";
|
||||
case SUCCESS -> "";
|
||||
case NO_CUSTOMER_PACKAGE -> "未绑定套餐";
|
||||
case NO_ACTIVE_PACKAGE_VERSION -> "无生效套餐版本";
|
||||
case ALREADY_IN_AR_BILL -> "已生成应收账单";
|
||||
case PRODUCT_ARCHIVE_MISSING -> "产品档案缺失";
|
||||
case PRICE_NOT_HIT -> "未命中价格";
|
||||
case ZONE_PRICE_NOT_HIT -> "分区无价格";
|
||||
case POSTCODE_REQUIRED -> "邮编必填";
|
||||
case POSTCODE_ZONE_NOT_FOUND -> "邮编未匹配分区";
|
||||
case ORACLE_ORDER_NOT_FOUND -> "上游订单不存在";
|
||||
case ORACLE_ORDERNO_EMPTY -> "上游订单号为空";
|
||||
case ORACLE_CUSTOMER_EMPTY -> "上游客户为空";
|
||||
case ORACLE_PRODUCT_EMPTY -> "上游产品为空";
|
||||
case ORACLE_WEIGHT_INVALID -> "上游重量无效";
|
||||
case BILL_TIME_EMPTY -> "计费时间为空";
|
||||
case REFUND_ORDER_UNRESOLVED -> "退费订单无法解析";
|
||||
case CUSTOMER_CODE_REQUIRED -> "客户代码为空";
|
||||
case CUSTOMER_DISABLED -> "客户已停用";
|
||||
case FREIGHT_MISSING -> "运费为空";
|
||||
case UNKNOWN -> "未知失败";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public class PrcIncomeBill implements Serializable
|
||||
private BigDecimal salesAmount;
|
||||
/** 应收合计(当前等于销售价) */
|
||||
private BigDecimal incomeTotal;
|
||||
/** 是否退货:NONE未退货/REFUNDED已退货 */
|
||||
private String refundStatus;
|
||||
/** 业务员/销售员 */
|
||||
private String belongSaler;
|
||||
/** 销售员编码 */
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -30,6 +29,8 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String transferNo;
|
||||
/** 批量查询:多个转单号精确匹配(仅查询用,非表字段) */
|
||||
private List<String> transferNos;
|
||||
/** 批量查询:订单号、运单号、转单号任一命中(仅查询用,非表字段) */
|
||||
private List<String> orderIdentifiers;
|
||||
/** 所属销售员 */
|
||||
private String belongSaler;
|
||||
/** 销售员编码 */
|
||||
@@ -62,8 +63,12 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String warehouseCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderTime;
|
||||
/** 应收来源查询专用:从 prc_order_bill_ext.pickup_date 读取的收件时间。 */
|
||||
@JsonIgnore
|
||||
/** 上游订单状态 ORDER_STATUS */
|
||||
private Integer orderStatus;
|
||||
/** 是否收货:0否/1是 */
|
||||
private Integer blPickup;
|
||||
/** 收货时间 PICKUP_DATE */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date pickupDate;
|
||||
private String orderRemark;
|
||||
private String productCode;
|
||||
@@ -118,6 +123,8 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String refundStatus;
|
||||
/** 计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段;软失败时金额为0且不落费用明细 */
|
||||
private Integer billingFailStatus;
|
||||
/** 失败原因 */
|
||||
private String errorMessage;
|
||||
/** \u6279\u91cf\u67e5\u8be2\uff1a\u591a\u4e2a\u8ba1\u8d39\u5931\u8d25\u72b6\u6001\uff08\u4ec5\u67e5\u8be2\u7528\uff0c\u975e\u8868\u5b57\u6bb5\uff09 */
|
||||
private List<Integer> billingFailStatuses;
|
||||
private String roundMode;
|
||||
|
||||
+2
@@ -25,6 +25,8 @@ public class BatchAddIncomeBillFeeRequest implements Serializable
|
||||
private List<String> orderNos;
|
||||
private List<String> trackingNos;
|
||||
private List<String> transferNos;
|
||||
/** 订单号、运单号、转单号任一命中 */
|
||||
private List<String> orderIdentifiers;
|
||||
private List<String> customerCodes;
|
||||
private List<String> productCodes;
|
||||
private String countryCode;
|
||||
|
||||
+2
@@ -16,6 +16,8 @@ public class ChargeWeightTemplateRequest implements Serializable
|
||||
private List<String> orderNos;
|
||||
private List<String> trackingNos;
|
||||
private List<String> transferNos;
|
||||
/** 订单号、运单号、转单号任一命中 */
|
||||
private List<String> orderIdentifiers;
|
||||
private List<String> customerCodes;
|
||||
private List<String> productCodes;
|
||||
private String creditArrearsStatus;
|
||||
|
||||
@@ -19,6 +19,8 @@ public class RecalcBillPriceRequest implements Serializable
|
||||
private List<String> orderNos;
|
||||
private List<String> trackingNos;
|
||||
private List<String> transferNos;
|
||||
/** 订单号、运单号、转单号任一命中 */
|
||||
private List<String> orderIdentifiers;
|
||||
private List<String> customerCodes;
|
||||
private List<String> productCodes;
|
||||
private String countryCode;
|
||||
|
||||
@@ -50,6 +50,13 @@ public class TmsPriceBillRequest extends TmsPriceQuoteRequest
|
||||
private String warehouseCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderTime;
|
||||
/** Upstream TAB_ORDER.ORDER_STATUS */
|
||||
private Integer orderStatus;
|
||||
/** Upstream TAB_ORDER.BL_PICKUP 0/1 */
|
||||
private Integer blPickup;
|
||||
/** Upstream TAB_ORDER.PICKUP_DATE */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date pickupDate;
|
||||
private String orderRemark;
|
||||
/** 上游 TAB_ORDER 整行,用于写入 prc_order_bill_ext 扩展字段 */
|
||||
private Map<String, Object> upstreamRow;
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
package com.bms.price.enums;
|
||||
|
||||
/**
|
||||
* prc_order_bill.billing_fail_status 计费失败状态。
|
||||
* 0=成功;1=未匹配产品;2=未匹配国家;3=未匹配重量段。
|
||||
* prc_order_bill.billing_fail_status \u8ba1\u8d39\u5931\u8d25\u72b6\u6001\uff08\u6570\u5b57\uff09\u3002
|
||||
* 0\u6210\u529f\uff1b1\u672a\u5339\u914d\u4ea7\u54c1\uff1b2\u672a\u5339\u914d\u56fd\u5bb6\uff1b3\u672a\u5339\u914d\u91cd\u91cf\u6bb5\u3002
|
||||
* 4 \u8d77\u4e0e prc_billing_task.fail_code \u4e00\u4e00\u5bf9\u5e94\u3002
|
||||
* \u4ec5 0/1/2/3 \u5199\u5165 prc_income_bill\uff1b\u4ec5 0 \u5199\u5165 prc_income_bill_fee\u3002
|
||||
*/
|
||||
public enum BillBillingFailStatus
|
||||
{
|
||||
SUCCESS(0),
|
||||
PRODUCT(1),
|
||||
COUNTRY(2),
|
||||
WEIGHT(3);
|
||||
WEIGHT(3),
|
||||
NO_CUSTOMER_PACKAGE(4),
|
||||
NO_ACTIVE_PACKAGE_VERSION(5),
|
||||
ALREADY_IN_AR_BILL(6),
|
||||
PRODUCT_ARCHIVE_MISSING(7),
|
||||
PRICE_NOT_HIT(8),
|
||||
ZONE_PRICE_NOT_HIT(9),
|
||||
POSTCODE_REQUIRED(10),
|
||||
POSTCODE_ZONE_NOT_FOUND(11),
|
||||
ORACLE_ORDER_NOT_FOUND(12),
|
||||
ORACLE_ORDERNO_EMPTY(13),
|
||||
ORACLE_CUSTOMER_EMPTY(14),
|
||||
ORACLE_PRODUCT_EMPTY(15),
|
||||
ORACLE_WEIGHT_INVALID(16),
|
||||
BILL_TIME_EMPTY(17),
|
||||
REFUND_ORDER_UNRESOLVED(18),
|
||||
CUSTOMER_CODE_REQUIRED(19),
|
||||
CUSTOMER_DISABLED(20),
|
||||
FREIGHT_MISSING(21),
|
||||
UNKNOWN(22);
|
||||
|
||||
private final int code;
|
||||
|
||||
@@ -44,28 +65,99 @@ public enum BillBillingFailStatus
|
||||
return code != null && code.intValue() > SUCCESS.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map soft-fail status to prc_billing_task.fail_code.
|
||||
*/
|
||||
/** 0/1/2/3 \u624d\u843d prc_income_bill\u3002 */
|
||||
public static boolean storesIncomeBill(Integer code)
|
||||
{
|
||||
int value = code == null ? SUCCESS.code : code.intValue();
|
||||
return value == SUCCESS.code || value == PRODUCT.code || value == COUNTRY.code || value == WEIGHT.code;
|
||||
}
|
||||
|
||||
/** \u4ec5 0 \u843d prc_income_bill_fee\u3002 */
|
||||
public static boolean storesIncomeFee(Integer code)
|
||||
{
|
||||
return code == null || code.intValue() == SUCCESS.code;
|
||||
}
|
||||
|
||||
public static int fromTaskFailCode(String failCode)
|
||||
{
|
||||
if (failCode == null || failCode.isEmpty())
|
||||
{
|
||||
return UNKNOWN.code;
|
||||
}
|
||||
for (BillBillingFailStatus status : values())
|
||||
{
|
||||
if (status == SUCCESS)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (failCode.equals(status.toTaskFailCode()))
|
||||
{
|
||||
return status.code;
|
||||
}
|
||||
}
|
||||
return UNKNOWN.code;
|
||||
}
|
||||
|
||||
public String toTaskFailCode()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case SUCCESS:
|
||||
return null;
|
||||
case PRODUCT:
|
||||
return BillTaskFailCode.PRODUCT_NOT_HIT.getCode();
|
||||
case COUNTRY:
|
||||
return BillTaskFailCode.COUNTRY_NOT_HIT.getCode();
|
||||
case WEIGHT:
|
||||
return BillTaskFailCode.WEIGHT_NOT_HIT.getCode();
|
||||
case NO_CUSTOMER_PACKAGE:
|
||||
return BillTaskFailCode.NO_CUSTOMER_PACKAGE.getCode();
|
||||
case NO_ACTIVE_PACKAGE_VERSION:
|
||||
return BillTaskFailCode.NO_ACTIVE_PACKAGE_VERSION.getCode();
|
||||
case ALREADY_IN_AR_BILL:
|
||||
return BillTaskFailCode.ALREADY_IN_AR_BILL.getCode();
|
||||
case PRODUCT_ARCHIVE_MISSING:
|
||||
return BillTaskFailCode.PRODUCT_ARCHIVE_MISSING.getCode();
|
||||
case PRICE_NOT_HIT:
|
||||
return BillTaskFailCode.PRICE_NOT_HIT.getCode();
|
||||
case ZONE_PRICE_NOT_HIT:
|
||||
return BillTaskFailCode.ZONE_PRICE_NOT_HIT.getCode();
|
||||
case POSTCODE_REQUIRED:
|
||||
return BillTaskFailCode.POSTCODE_REQUIRED.getCode();
|
||||
case POSTCODE_ZONE_NOT_FOUND:
|
||||
return BillTaskFailCode.POSTCODE_ZONE_NOT_FOUND.getCode();
|
||||
case ORACLE_ORDER_NOT_FOUND:
|
||||
return BillTaskFailCode.ORACLE_ORDER_NOT_FOUND.getCode();
|
||||
case ORACLE_ORDERNO_EMPTY:
|
||||
return BillTaskFailCode.ORACLE_ORDERNO_EMPTY.getCode();
|
||||
case ORACLE_CUSTOMER_EMPTY:
|
||||
return BillTaskFailCode.ORACLE_CUSTOMER_EMPTY.getCode();
|
||||
case ORACLE_PRODUCT_EMPTY:
|
||||
return BillTaskFailCode.ORACLE_PRODUCT_EMPTY.getCode();
|
||||
case ORACLE_WEIGHT_INVALID:
|
||||
return BillTaskFailCode.ORACLE_WEIGHT_INVALID.getCode();
|
||||
case BILL_TIME_EMPTY:
|
||||
return BillTaskFailCode.BILL_TIME_EMPTY.getCode();
|
||||
case REFUND_ORDER_UNRESOLVED:
|
||||
return BillTaskFailCode.REFUND_ORDER_UNRESOLVED.getCode();
|
||||
case CUSTOMER_CODE_REQUIRED:
|
||||
return BillTaskFailCode.CUSTOMER_CODE_REQUIRED.getCode();
|
||||
case CUSTOMER_DISABLED:
|
||||
return BillTaskFailCode.CUSTOMER_DISABLED.getCode();
|
||||
case FREIGHT_MISSING:
|
||||
return BillTaskFailCode.FREIGHT_MISSING.getCode();
|
||||
default:
|
||||
return BillTaskFailCode.UNKNOWN.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static String toTaskFailCode(Integer billingFailStatus)
|
||||
{
|
||||
BillBillingFailStatus status = of(billingFailStatus);
|
||||
if (status == null || status == SUCCESS)
|
||||
if (status == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (status == PRODUCT)
|
||||
{
|
||||
return BillTaskFailCode.PRODUCT_NOT_HIT.getCode();
|
||||
}
|
||||
if (status == COUNTRY)
|
||||
{
|
||||
return BillTaskFailCode.COUNTRY_NOT_HIT.getCode();
|
||||
}
|
||||
if (status == WEIGHT)
|
||||
{
|
||||
return BillTaskFailCode.WEIGHT_NOT_HIT.getCode();
|
||||
}
|
||||
return BillTaskFailCode.UNKNOWN.getCode();
|
||||
return status.toTaskFailCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ public interface PrcBillingTaskMapper
|
||||
/** CAS: WAITING -> processing claim not needed; writeback SUCCESS/FAILED */
|
||||
int updateBillingResult(PrcBillingTask task);
|
||||
|
||||
/** Delete the queue row once prc_order_bill is saved. transfer_no is the unique key. */
|
||||
int deleteByTransferNo(@Param("transferNo") String transferNo);
|
||||
|
||||
/** Count tasks for filter retry (customer + bill_time window). */
|
||||
int countTaskIdsByFilter(PrcBillingTask query);
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@ public class OracleOrderBillMapper
|
||||
{
|
||||
req.setOrderTime(createDate);
|
||||
}
|
||||
req.setOrderStatus(firstInt(row, "ORDER_STATUS"));
|
||||
req.setBlPickup(firstInt(row, "BL_PICKUP"));
|
||||
req.setPickupDate(firstDate(row, "PICKUP_DATE"));
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,12 @@ public interface IPrcOrderBillService
|
||||
{
|
||||
TmsPriceQuoteResult bill(TmsPriceBillRequest request);
|
||||
|
||||
/**
|
||||
* Persist prc_order_bill and ext when billing cannot produce a quote.
|
||||
* Status outside 0/1/2/3 does not write prc_income_bill.
|
||||
*/
|
||||
void persistFailedSnapshot(TmsPriceBillRequest request, int billingFailStatus, String errorMessage);
|
||||
|
||||
/**
|
||||
* 退费:保留原费用明细,在 prc_income_bill_fee 增加一条负金额(feeType=REFUND),重算应收并回补余额。
|
||||
*
|
||||
|
||||
+91
-39
@@ -112,7 +112,7 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
{
|
||||
PrcBillingTask task = createWaitingTask(request, "monitor");
|
||||
processTask(task.getTaskId());
|
||||
return billingTaskMapper.selectPrcBillingTaskById(task.getTaskId());
|
||||
return taskAfterProcess(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +129,7 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
{
|
||||
processTaskWithUpstreamRow(task, upstreamRow);
|
||||
}
|
||||
return billingTaskMapper.selectPrcBillingTaskById(task.getTaskId());
|
||||
return taskAfterProcess(task);
|
||||
}
|
||||
|
||||
/** 订单列表已拉回 Oracle 全行时直接计费,跳过 findMainOrder 的多次公网往返。 */
|
||||
@@ -138,7 +138,8 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
Long taskId = task.getTaskId();
|
||||
if (BillTaskSuccessStatus.SUCCESS.getCode().equals(task.getSuccessStatus()))
|
||||
{
|
||||
log.info("billing task already success taskId={}", taskId);
|
||||
log.info("billing task already success, drop queue row taskId={}", taskId);
|
||||
deleteTaskAfterBillSaved(task);
|
||||
return;
|
||||
}
|
||||
try
|
||||
@@ -212,14 +213,19 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
}
|
||||
if (BillTaskSuccessStatus.SUCCESS.getCode().equals(task.getSuccessStatus()))
|
||||
{
|
||||
log.info("billing task already success taskId={}", taskId);
|
||||
log.info("billing task already success, drop queue row taskId={}", taskId);
|
||||
deleteTaskAfterBillSaved(task);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (task.getBillTime() == null)
|
||||
{
|
||||
throw new ServiceException("task billTime empty (billing time from mobile required)");
|
||||
prcOrderBillService.persistFailedSnapshot(requestFromTask(task),
|
||||
BillBillingFailStatus.fromTaskFailCode(BillTaskFailCode.BILL_TIME_EMPTY.getCode()),
|
||||
"task billTime empty (billing time from mobile required)");
|
||||
deleteTaskAfterBillSaved(task);
|
||||
return;
|
||||
}
|
||||
BillTaskType type = BillTaskType.fromCode(task.getBillType());
|
||||
if (BillTaskType.REFUND == type)
|
||||
@@ -227,9 +233,8 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
String refundOrderNo = resolveRefundOrderNo(task);
|
||||
log.info("billing task REFUND path taskId={} orderNo={}", taskId, refundOrderNo);
|
||||
fillCustomerCodeFromBill(task, refundOrderNo);
|
||||
TmsPriceQuoteResult result = refundWithDbRetry(taskId, refundOrderNo);
|
||||
writeback(task, BillTaskSuccessStatus.SUCCESS, result == null ? null : result.getBillId(),
|
||||
null, null);
|
||||
refundWithDbRetry(taskId, refundOrderNo);
|
||||
deleteTaskAfterBillSaved(task);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -237,7 +242,11 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
task.getTransferNo(), task.getOrderNo(), task.getTrackingNo());
|
||||
if (row == null || row.isEmpty())
|
||||
{
|
||||
throw new ServiceException("Oracle main order not found");
|
||||
prcOrderBillService.persistFailedSnapshot(requestFromTask(task),
|
||||
BillBillingFailStatus.fromTaskFailCode(BillTaskFailCode.ORACLE_ORDER_NOT_FOUND.getCode()),
|
||||
"Oracle main order not found");
|
||||
deleteTaskAfterBillSaved(task);
|
||||
return;
|
||||
}
|
||||
billFromUpstreamRow(task, row);
|
||||
}
|
||||
@@ -257,23 +266,10 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
Long taskId = task.getTaskId();
|
||||
TmsPriceBillRequest billReq = oracleOrderBillMapper.toBillRequest(row,
|
||||
billingProperties.getOracle().getDefaultProductCode());
|
||||
if (billReq == null || StringUtils.isEmpty(billReq.getOrderNo()))
|
||||
if (billReq == null)
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped orderNo empty");
|
||||
billReq = requestFromTask(task);
|
||||
}
|
||||
if (StringUtils.isEmpty(billReq.getCustomerCode()))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped customerCode empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(billReq.getProductCode()))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped productCode empty");
|
||||
}
|
||||
if (billReq.getWeight() == null || billReq.getWeight().signum() <= 0)
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped weight invalid");
|
||||
}
|
||||
// Identity keys from mobile inbound are source of truth; do not replace with Oracle remap.
|
||||
if (StringUtils.isNotEmpty(task.getTransferNo()))
|
||||
{
|
||||
billReq.setTransferNo(task.getTransferNo());
|
||||
@@ -286,26 +282,61 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
{
|
||||
billReq.setOrderNo(task.getOrderNo());
|
||||
}
|
||||
// Billing datetime from task.bill_time (keep HH:mm:ss), not Oracle CREATE_DATE.
|
||||
billReq.setBizDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(task.getBillTime()));
|
||||
if (task.getBillTime() != null)
|
||||
{
|
||||
billReq.setBizDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(task.getBillTime()));
|
||||
}
|
||||
task.setCustomerCode(billReq.getCustomerCode());
|
||||
try
|
||||
{
|
||||
if (StringUtils.isEmpty(billReq.getOrderNo()) && StringUtils.isEmpty(billReq.getTransferNo()))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped orderNo empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(billReq.getCustomerCode()))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped customerCode empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(billReq.getProductCode()))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped productCode empty");
|
||||
}
|
||||
if (billReq.getWeight() == null || billReq.getWeight().signum() <= 0)
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped weight invalid");
|
||||
}
|
||||
billWithDbRetry(taskId, billReq);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
int failStatus = BillBillingFailStatus.fromTaskFailCode(BillTaskFailCode.resolveCode(e.getMessage()));
|
||||
prcOrderBillService.persistFailedSnapshot(billReq, failStatus, cut(e.getMessage(), 900));
|
||||
}
|
||||
deleteTaskAfterBillSaved(task);
|
||||
}
|
||||
|
||||
TmsPriceQuoteResult result = billWithDbRetry(taskId, billReq);
|
||||
// Soft-fail (product/country/weight miss): order bill already persisted with zero amounts;
|
||||
// still mark task FAILED so it appears in abnormal-task list for retry/ops.
|
||||
if (result != null && BillBillingFailStatus.isFail(result.getBillingFailStatus()))
|
||||
private TmsPriceBillRequest requestFromTask(PrcBillingTask task)
|
||||
{
|
||||
TmsPriceBillRequest req = new TmsPriceBillRequest();
|
||||
req.setTransferNo(task.getTransferNo());
|
||||
req.setTrackingNo(task.getTrackingNo());
|
||||
req.setOrderNo(StringUtils.isNotEmpty(task.getOrderNo()) ? task.getOrderNo() : task.getTransferNo());
|
||||
req.setCustomerCode(task.getCustomerCode());
|
||||
if (task.getBillTime() != null)
|
||||
{
|
||||
String err = StringUtils.isNotEmpty(result.getMatchMessage())
|
||||
? result.getMatchMessage()
|
||||
: ("billingFailStatus=" + result.getBillingFailStatus());
|
||||
writeback(task, BillTaskSuccessStatus.FAILED, result.getBillId(), cut(err, 900),
|
||||
BillBillingFailStatus.toTaskFailCode(result.getBillingFailStatus()));
|
||||
req.setBizDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(task.getBillTime()));
|
||||
}
|
||||
else
|
||||
return req;
|
||||
}
|
||||
|
||||
/** Order bill is the failure record; drop the queue row by transfer_no. */
|
||||
private void deleteTaskAfterBillSaved(PrcBillingTask task)
|
||||
{
|
||||
if (task == null || StringUtils.isEmpty(task.getTransferNo()))
|
||||
{
|
||||
writeback(task, BillTaskSuccessStatus.SUCCESS, result == null ? null : result.getBillId(),
|
||||
null, null);
|
||||
throw new ServiceException("billing task transferNo empty, cannot delete after bill save");
|
||||
}
|
||||
billingTaskMapper.deleteByTransferNo(task.getTransferNo().trim());
|
||||
}
|
||||
|
||||
/** Resolve orderNo for REFUND from task keys or existing bill (no Oracle quote required). */
|
||||
@@ -474,7 +505,28 @@ public class PrcBillingTaskServiceImpl implements IPrcBillingTaskService
|
||||
billingTaskMapper.updateBillingResult(reset);
|
||||
|
||||
processTask(taskId);
|
||||
return billingTaskMapper.selectPrcBillingTaskById(taskId);
|
||||
return taskAfterProcess(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bill save deletes the queue row. Callers still need a SUCCESS result when the row is gone.
|
||||
* A remaining row means the bill was not saved and the task was marked FAILED.
|
||||
*/
|
||||
private PrcBillingTask taskAfterProcess(PrcBillingTask task)
|
||||
{
|
||||
if (task == null || task.getTaskId() == null)
|
||||
{
|
||||
return task;
|
||||
}
|
||||
PrcBillingTask saved = billingTaskMapper.selectPrcBillingTaskById(task.getTaskId());
|
||||
if (saved != null)
|
||||
{
|
||||
return saved;
|
||||
}
|
||||
task.setSuccessStatus(BillTaskSuccessStatus.SUCCESS.getCode());
|
||||
task.setFailCode(null);
|
||||
task.setErrorMessage(null);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+164
-8
@@ -272,6 +272,11 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
refundFee.setManualFlag("0");
|
||||
incomeBillFeeMapper.insertPrcIncomeBillFee(refundFee);
|
||||
bill.setRefundStatus(BillRefundStatus.REFUNDED.getCode());
|
||||
if (incomeBill != null)
|
||||
{
|
||||
incomeBill.setRefundStatus(BillRefundStatus.REFUNDED.getCode());
|
||||
incomeBillMapper.updatePrcIncomeBill(incomeBill);
|
||||
}
|
||||
|
||||
TmsPriceQuoteResult result = refreshBillIncomeTotals(bill);
|
||||
JSONObject req = new JSONObject();
|
||||
@@ -619,7 +624,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
}
|
||||
String mode = normalizeRecalcMode(request.getMode());
|
||||
List<PrcOrderBill> bills = resolveListScopedBills(request.getScopeMode(), request.getBillIds(),
|
||||
request.getOrderNos(), request.getTrackingNos(), request.getTransferNos(),
|
||||
request.getOrderNos(), request.getTrackingNos(), request.getTransferNos(), request.getOrderIdentifiers(),
|
||||
request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(),
|
||||
request.getCreditArrearsStatus(), request.getBeginBizDate(), request.getEndBizDate(),
|
||||
"price.bill.recalc.filterRequired", "price.bill.recalc.scopeRequired",
|
||||
@@ -779,7 +784,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
throw new ServiceException(MessageUtils.message("price.bill.fee.batch.filterRequired"));
|
||||
}
|
||||
return resolveListScopedBills(request.getScopeMode(), request.getBillIds(),
|
||||
request.getOrderNos(), request.getTrackingNos(), request.getTransferNos(),
|
||||
request.getOrderNos(), request.getTrackingNos(), request.getTransferNos(), request.getOrderIdentifiers(),
|
||||
request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(),
|
||||
request.getCreditArrearsStatus(), request.getBeginBizDate(), request.getEndBizDate(),
|
||||
"price.bill.fee.batch.filterRequired", "price.bill.fee.batch.scopeRequired",
|
||||
@@ -791,7 +796,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
* scopeMode: SELECTED uses billIds; ALL uses filters (at least one required).
|
||||
*/
|
||||
private List<PrcOrderBill> resolveListScopedBills(String scopeModeRaw, List<String> billIdsRaw,
|
||||
List<String> orderNosRaw, List<String> trackingNosRaw, List<String> transferNosRaw,
|
||||
List<String> orderNosRaw, List<String> trackingNosRaw, List<String> transferNosRaw, List<String> orderIdentifiersRaw,
|
||||
List<String> customerCodesRaw, List<String> productCodesRaw, String countryCodeRaw,
|
||||
String creditArrearsStatusRaw, String beginBizDateRaw, String endBizDateRaw,
|
||||
String msgFilter, String msgScope, String msgSelected)
|
||||
@@ -804,6 +809,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
List<String> orderNos = normalizeCodeList(orderNosRaw);
|
||||
List<String> trackingNos = normalizeCodeList(trackingNosRaw);
|
||||
List<String> transferNos = normalizeCodeList(transferNosRaw);
|
||||
List<String> orderIdentifiers = normalizeCodeList(orderIdentifiersRaw);
|
||||
List<String> customerCodes = normalizeCodeList(customerCodesRaw);
|
||||
List<String> productCodes = normalizeCodeList(productCodesRaw);
|
||||
String creditArrearsStatus = StringUtils.isEmpty(creditArrearsStatusRaw) ? null
|
||||
@@ -815,6 +821,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
boolean hasFilter = (orderNos != null && !orderNos.isEmpty())
|
||||
|| (trackingNos != null && !trackingNos.isEmpty())
|
||||
|| (transferNos != null && !transferNos.isEmpty())
|
||||
|| (orderIdentifiers != null && !orderIdentifiers.isEmpty())
|
||||
|| (customerCodes != null && !customerCodes.isEmpty())
|
||||
|| (productCodes != null && !productCodes.isEmpty())
|
||||
|| StringUtils.isNotEmpty(creditArrearsStatus)
|
||||
@@ -840,6 +847,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
query.setOrderNos(orderNos);
|
||||
query.setTrackingNos(trackingNos);
|
||||
query.setTransferNos(transferNos);
|
||||
query.setOrderIdentifiers(orderIdentifiers);
|
||||
query.setCustomerCodes(customerCodes);
|
||||
query.setProductCodes(productCodes);
|
||||
query.setCreditArrearsStatus(creditArrearsStatus);
|
||||
@@ -1297,6 +1305,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
bill.setDestPostcode(request.getDestPostcode());
|
||||
bill.setWarehouseCode(request.getWarehouseCode());
|
||||
bill.setOrderTime(request.getOrderTime());
|
||||
bill.setOrderStatus(request.getOrderStatus());
|
||||
bill.setBlPickup(request.getBlPickup());
|
||||
bill.setPickupDate(request.getPickupDate());
|
||||
bill.setOrderRemark(request.getOrderRemark());
|
||||
bill.setProductCode(result.getProductCode());
|
||||
bill.setProductName(result.getProductName());
|
||||
@@ -1328,7 +1339,18 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
billingFailStatus = Integer.valueOf(BillBillingFailStatus.SUCCESS.getCode());
|
||||
}
|
||||
bill.setBillingFailStatus(billingFailStatus);
|
||||
boolean billingFailed = BillBillingFailStatus.isFail(billingFailStatus);
|
||||
boolean storeIncome = BillBillingFailStatus.storesIncomeBill(billingFailStatus);
|
||||
boolean storeFee = BillBillingFailStatus.storesIncomeFee(billingFailStatus);
|
||||
boolean billingFailed = !storeFee;
|
||||
if (billingFailed)
|
||||
{
|
||||
String failText = result.getMatchMessage();
|
||||
bill.setErrorMessage(failText == null ? null : (failText.length() > 1000 ? failText.substring(0, 1000) : failText));
|
||||
}
|
||||
else
|
||||
{
|
||||
bill.setErrorMessage(null);
|
||||
}
|
||||
if (billingFailed)
|
||||
{
|
||||
// Soft-fail: persist order snapshot with zero amounts; skip fee detail rows.
|
||||
@@ -1399,9 +1421,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
else
|
||||
{
|
||||
orderBillMapper.updatePrcOrderBill(bill);
|
||||
if (billingFailed)
|
||||
if (!storeFee)
|
||||
{
|
||||
// Soft-fail rebill: clear all fee details (no fee rows for failed billing).
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
else
|
||||
@@ -1412,8 +1433,17 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
// Payable not in scope yet: skip prc_pay_bill_fee cleanup/write.
|
||||
// payBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
PrcIncomeBill incomeBill = persistIncomeBill(bill, result);
|
||||
if (!billingFailed)
|
||||
PrcIncomeBill incomeBill = null;
|
||||
if (storeIncome)
|
||||
{
|
||||
incomeBill = persistIncomeBill(bill, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
incomeBillMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
if (storeFee)
|
||||
{
|
||||
List<PrcIncomeBillFee> fees = toFeeRows(bill.getBillId(),
|
||||
incomeBill == null ? null : incomeBill.getIncomeBillId(), result);
|
||||
@@ -1735,6 +1765,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
row.setRedlineAmount(result == null ? null : result.getRedlineAmount());
|
||||
row.setSalesAmount(sales);
|
||||
row.setIncomeTotal(sales);
|
||||
row.setRefundStatus(StringUtils.isEmpty(bill.getRefundStatus())
|
||||
? BillRefundStatus.NONE.getCode() : bill.getRefundStatus());
|
||||
row.setBelongSaler(bill.getBelongSaler());
|
||||
row.setBelongSalerCode(bill.getBelongSalerCode());
|
||||
row.setSettlementMan(bill.getSettlementMan());
|
||||
@@ -2213,6 +2245,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
boolean hasFilter = (orderNos != null && !orderNos.isEmpty())
|
||||
|| (trackingNos != null && !trackingNos.isEmpty())
|
||||
|| (transferNos != null && !transferNos.isEmpty())
|
||||
|| (request.getOrderIdentifiers() != null && !normalizeCodeList(request.getOrderIdentifiers()).isEmpty())
|
||||
|| (customerCodes != null && !customerCodes.isEmpty())
|
||||
|| (productCodes != null && !productCodes.isEmpty())
|
||||
|| StringUtils.isNotEmpty(creditArrearsStatus)
|
||||
@@ -2226,6 +2259,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
query.setOrderNos(orderNos);
|
||||
query.setTrackingNos(trackingNos);
|
||||
query.setTransferNos(transferNos);
|
||||
query.setOrderIdentifiers(normalizeCodeList(request.getOrderIdentifiers()));
|
||||
query.setCustomerCodes(customerCodes);
|
||||
query.setProductCodes(productCodes);
|
||||
query.setCreditArrearsStatus(creditArrearsStatus);
|
||||
@@ -2361,6 +2395,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
req.setConsigneeName(bill.getConsigneeName());
|
||||
req.setWarehouseCode(bill.getWarehouseCode());
|
||||
req.setOrderTime(bill.getOrderTime());
|
||||
req.setOrderStatus(bill.getOrderStatus());
|
||||
req.setBlPickup(bill.getBlPickup());
|
||||
req.setPickupDate(bill.getPickupDate());
|
||||
req.setOrderRemark(bill.getOrderRemark());
|
||||
if (bill.getTmsId() != null)
|
||||
{
|
||||
@@ -2389,4 +2426,123 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void persistFailedSnapshot(TmsPriceBillRequest request, int billingFailStatus, String errorMessage)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ServiceException("order snapshot request empty");
|
||||
}
|
||||
String orderNo = request.getOrderNo();
|
||||
if (StringUtils.isEmpty(orderNo))
|
||||
{
|
||||
orderNo = request.getTransferNo();
|
||||
}
|
||||
if (StringUtils.isEmpty(orderNo))
|
||||
{
|
||||
throw new ServiceException("Oracle order mapped orderNo empty");
|
||||
}
|
||||
orderNo = orderNo.trim();
|
||||
request.setOrderNo(orderNo);
|
||||
PrcOrderBill existed = orderBillMapper.selectPrcOrderBillByOrderNo(orderNo);
|
||||
assertOrderBillEditable(existed);
|
||||
BigDecimal previousIncome = existed == null ? BigDecimal.ZERO : nz(existed.getIncomeTotal());
|
||||
PrcOrderBill bill = existed == null ? new PrcOrderBill() : existed;
|
||||
if (existed == null)
|
||||
{
|
||||
bill.setBillId(UUID.randomUUID().toString());
|
||||
bill.setArBillStatus(ArOrderBillStatus.UNBILLED.getCode());
|
||||
bill.setRefundStatus(BillRefundStatus.NONE.getCode());
|
||||
}
|
||||
bill.setBillKey(Md5Utils.hash(orderNo));
|
||||
bill.setOrderNo(orderNo);
|
||||
bill.setCustomerId(request.getCustomerId() == null ? Long.valueOf(0L) : request.getCustomerId());
|
||||
bill.setCustomerCode(request.getCustomerCode());
|
||||
bill.setCustomerName(request.getCustomerName());
|
||||
bill.setTrackingNo(request.getTrackingNo());
|
||||
bill.setTransferNo(request.getTransferNo());
|
||||
resolveOrderStaffFromSysUser(request);
|
||||
bill.setBelongSaler(request.getBelongSaler());
|
||||
bill.setBelongSalerCode(request.getBelongSalerCode());
|
||||
bill.setSettlementMan(request.getSettlementMan());
|
||||
bill.setSettlementManCode(request.getSettlementManCode());
|
||||
bill.setServiceMan(request.getServiceMan());
|
||||
bill.setServiceManCode(request.getServiceManCode());
|
||||
if (StringUtils.isEmpty(bill.getCustomerActiveStatus()))
|
||||
{
|
||||
bill.setCustomerActiveStatus(BillCustomerActiveStatus.ACTIVE.getCode());
|
||||
}
|
||||
bill.setPieces(request.getPieces());
|
||||
bill.setActualWeight(request.getActualWeight());
|
||||
bill.setReceiveWeight(request.getReceiveWeight());
|
||||
bill.setVolume(request.getVolume());
|
||||
bill.setVolumeWeight(request.getVolumeWeight());
|
||||
bill.setPkgLength(request.getPkgLength());
|
||||
bill.setPkgWidth(request.getPkgWidth());
|
||||
bill.setPkgHeight(request.getPkgHeight());
|
||||
bill.setShipperName(request.getShipperName());
|
||||
bill.setConsigneeName(request.getConsigneeName());
|
||||
bill.setDestPostcode(request.getDestPostcode());
|
||||
bill.setWarehouseCode(request.getWarehouseCode());
|
||||
bill.setOrderTime(request.getOrderTime());
|
||||
bill.setOrderStatus(request.getOrderStatus());
|
||||
bill.setBlPickup(request.getBlPickup());
|
||||
bill.setPickupDate(request.getPickupDate());
|
||||
bill.setOrderRemark(request.getOrderRemark());
|
||||
bill.setProductCode(request.getProductCode());
|
||||
bill.setCountryCode(request.getCountryCode());
|
||||
bill.setCountryName(request.getCountryName());
|
||||
bill.setWeight(request.getWeight());
|
||||
Date bizDate = StringUtils.isEmpty(request.getBizDate()) ? new Date() : resolveBizDate(request.getBizDate());
|
||||
bill.setBizDate(bizDate);
|
||||
if (StringUtils.isEmpty(bill.getCurrency()))
|
||||
{
|
||||
bill.setCurrency("CNY");
|
||||
}
|
||||
if (StringUtils.isEmpty(bill.getRoundMode()))
|
||||
{
|
||||
bill.setRoundMode("HALF_UP");
|
||||
}
|
||||
bill.setFreightRate(BigDecimal.ZERO);
|
||||
bill.setBaseFreightRate(BigDecimal.ZERO);
|
||||
bill.setRegistrationFee(BigDecimal.ZERO);
|
||||
bill.setFreightAmount(BigDecimal.ZERO);
|
||||
bill.setSurchargeAmount(BigDecimal.ZERO);
|
||||
bill.setIncomeTotal(BigDecimal.ZERO);
|
||||
bill.setPayTotal(BigDecimal.ZERO);
|
||||
bill.setBillingFailStatus(Integer.valueOf(billingFailStatus));
|
||||
String failText = errorMessage == null ? "" : errorMessage.trim();
|
||||
if (failText.length() > 1000)
|
||||
{
|
||||
failText = failText.substring(0, 1000);
|
||||
}
|
||||
bill.setErrorMessage(failText);
|
||||
bill.setMatchMessage(failText);
|
||||
if (StringUtils.isEmpty(bill.getRefundStatus()))
|
||||
{
|
||||
bill.setRefundStatus(BillRefundStatus.NONE.getCode());
|
||||
}
|
||||
syncOrderCredit(bill, previousIncome, bill.getCustomerActiveStatus());
|
||||
if (existed == null)
|
||||
{
|
||||
orderBillMapper.insertPrcOrderBill(bill);
|
||||
}
|
||||
else
|
||||
{
|
||||
orderBillMapper.updatePrcOrderBill(bill);
|
||||
}
|
||||
if (BillBillingFailStatus.storesIncomeBill(Integer.valueOf(billingFailStatus)))
|
||||
{
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
persistIncomeBill(bill, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
incomeBillMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
persistBillExt(bill, request.getUpstreamRow());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +150,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
update_time = sysdate()
|
||||
where task_id = #{taskId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteByTransferNo">
|
||||
delete from prc_billing_task
|
||||
where transfer_no = #{transferNo}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
||||
@@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="redlineAmount" column="redline_amount" />
|
||||
<result property="salesAmount" column="sales_amount" />
|
||||
<result property="incomeTotal" column="income_total" />
|
||||
<result property="refundStatus" column="refund_status" />
|
||||
<result property="belongSaler" column="belong_saler" />
|
||||
<result property="belongSalerCode" column="belong_saler_code" />
|
||||
<result property="settlementMan" column="settlement_man" />
|
||||
@@ -24,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<sql id="selectPrcIncomeBillVo">
|
||||
select income_bill_id, bill_id, currency, product_amount, redline_amount,
|
||||
sales_amount, income_total,
|
||||
sales_amount, income_total, refund_status,
|
||||
belong_saler, belong_saler_code, settlement_man, settlement_man_code,
|
||||
create_by, create_time, update_by, update_time
|
||||
from prc_income_bill
|
||||
@@ -51,12 +52,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<insert id="insertPrcIncomeBill" parameterType="PrcIncomeBill">
|
||||
insert into prc_income_bill (
|
||||
income_bill_id, bill_id, currency, product_amount, redline_amount,
|
||||
sales_amount, income_total,
|
||||
sales_amount, income_total, refund_status,
|
||||
belong_saler, belong_saler_code, settlement_man, settlement_man_code,
|
||||
create_by, create_time
|
||||
) values (
|
||||
#{incomeBillId}, #{billId}, #{currency}, #{productAmount}, #{redlineAmount},
|
||||
#{salesAmount}, #{incomeTotal},
|
||||
#{salesAmount}, #{incomeTotal}, #{refundStatus},
|
||||
#{belongSaler}, #{belongSalerCode}, #{settlementMan}, #{settlementManCode},
|
||||
#{createBy}, sysdate()
|
||||
)
|
||||
@@ -69,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
redline_amount = #{redlineAmount},
|
||||
sales_amount = #{salesAmount},
|
||||
income_total = #{incomeTotal},
|
||||
refund_status = #{refundStatus},
|
||||
belong_saler = #{belongSaler},
|
||||
belong_saler_code = #{belongSalerCode},
|
||||
settlement_man = #{settlementMan},
|
||||
@@ -81,12 +83,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<insert id="upsertPrcIncomeBill" parameterType="PrcIncomeBill">
|
||||
insert into prc_income_bill (
|
||||
income_bill_id, bill_id, currency, product_amount, redline_amount,
|
||||
sales_amount, income_total,
|
||||
sales_amount, income_total, refund_status,
|
||||
belong_saler, belong_saler_code, settlement_man, settlement_man_code,
|
||||
create_by, create_time
|
||||
) values (
|
||||
#{incomeBillId}, #{billId}, #{currency}, #{productAmount}, #{redlineAmount},
|
||||
#{salesAmount}, #{incomeTotal},
|
||||
#{salesAmount}, #{incomeTotal}, #{refundStatus},
|
||||
#{belongSaler}, #{belongSalerCode}, #{settlementMan}, #{settlementManCode},
|
||||
#{createBy}, sysdate()
|
||||
)
|
||||
@@ -96,6 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
redline_amount = values(redline_amount),
|
||||
sales_amount = values(sales_amount),
|
||||
income_total = values(income_total),
|
||||
refund_status = values(refund_status),
|
||||
belong_saler = values(belong_saler),
|
||||
belong_saler_code = values(belong_saler_code),
|
||||
settlement_man = values(settlement_man),
|
||||
|
||||
@@ -72,6 +72,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="creditArrearsStatus" column="credit_arrears_status" />
|
||||
<result property="refundStatus" column="refund_status" />
|
||||
<result property="billingFailStatus" column="billing_fail_status" />
|
||||
<result property="errorMessage" column="error_message" />
|
||||
<result property="orderStatus" column="order_status" />
|
||||
<result property="blPickup" column="bl_pickup" />
|
||||
<result property="pickupDate" column="pickup_date" />
|
||||
<result property="roundMode" column="round_mode" />
|
||||
<result property="createBy" column="created_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@@ -95,7 +99,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
weight_min, weight_max, weight_label, effective_date, charge_weight,
|
||||
freight_rate, base_freight_rate, discount_percent, registration_fee,
|
||||
freight_amount, surcharge_amount, income_total, pay_total, currency, ar_bill_status,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, round_mode,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, error_message,
|
||||
order_status, bl_pickup, pickup_date, round_mode,
|
||||
created_by, create_time, updated_by, update_time
|
||||
from prc_order_bill p
|
||||
</sql>
|
||||
@@ -106,7 +111,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
p.belong_saler, p.belong_saler_code, p.settlement_man, p.settlement_man_code, p.service_man, p.service_man_code,
|
||||
p.customer_id, p.customer_code, p.customer_name,
|
||||
p.pieces, p.actual_weight, p.receive_weight, p.volume, p.volume_weight, p.pkg_length, p.pkg_width, p.pkg_height,
|
||||
p.shipper_name, p.consignee_name, p.dest_postcode, p.warehouse_code, p.order_time, e.pickup_date, p.order_remark,
|
||||
p.shipper_name, p.consignee_name, p.dest_postcode, p.warehouse_code, p.order_time, p.order_status, p.bl_pickup, p.pickup_date, p.order_remark,
|
||||
p.product_code, p.product_name, p.country_code, p.country_name, p.weight, p.biz_date, p.tms_id,
|
||||
p.match_source, p.match_message, p.price_set_id, p.price_set_code, p.price_set_name,
|
||||
p.set_type, p.bind_priority, p.customer_priceset_id, p.psv_id, p.psd_id, p.product_country_id,
|
||||
@@ -118,10 +123,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where f.bill_id = p.bill_id
|
||||
and f.ar_bill_status = #{feeArBillStatus}) as income_total,
|
||||
p.pay_total, p.currency, p.ar_bill_status,
|
||||
p.customer_active_status, p.credit_arrears_status, p.refund_status, p.billing_fail_status, p.round_mode,
|
||||
p.customer_active_status, p.credit_arrears_status, p.refund_status, p.billing_fail_status, p.error_message, p.round_mode,
|
||||
p.created_by, p.create_time, p.updated_by, p.update_time
|
||||
from prc_order_bill p
|
||||
left join prc_order_bill_ext e on e.bill_id = p.bill_id
|
||||
</sql>
|
||||
|
||||
<select id="selectPrcOrderBillByOrderNo" parameterType="String" resultMap="PrcOrderBillResult">
|
||||
@@ -281,6 +285,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
#{rno}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="orderIdentifiers != null and orderIdentifiers.size() > 0">
|
||||
AND (
|
||||
p.order_no IN
|
||||
<foreach collection="orderIdentifiers" item="idn" open="(" separator="," close=")">
|
||||
#{idn}
|
||||
</foreach>
|
||||
OR p.tracking_no IN
|
||||
<foreach collection="orderIdentifiers" item="idn" open="(" separator="," close=")">
|
||||
#{idn}
|
||||
</foreach>
|
||||
OR p.transfer_no IN
|
||||
<foreach collection="orderIdentifiers" item="idn" open="(" separator="," close=")">
|
||||
#{idn}
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="customerActiveStatus != null and customerActiveStatus != ''">
|
||||
AND p.customer_active_status = #{customerActiveStatus}
|
||||
</if>
|
||||
@@ -544,28 +564,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
belong_saler, belong_saler_code, settlement_man, settlement_man_code, service_man, service_man_code,
|
||||
customer_id, customer_code, customer_name,
|
||||
pieces, actual_weight, receive_weight, volume, volume_weight, pkg_length, pkg_width, pkg_height,
|
||||
shipper_name, consignee_name, dest_postcode, warehouse_code, order_time, order_remark,
|
||||
shipper_name, consignee_name, dest_postcode, warehouse_code, order_time, order_status, bl_pickup, pickup_date, order_remark,
|
||||
product_code, product_name, country_code, country_name, weight, biz_date, tms_id,
|
||||
match_source, match_message, price_set_id, price_set_code, price_set_name,
|
||||
set_type, bind_priority, customer_priceset_id, psv_id, psd_id, product_country_id,
|
||||
weight_min, weight_max, weight_label, effective_date, charge_weight,
|
||||
freight_rate, base_freight_rate, discount_percent, registration_fee,
|
||||
freight_amount, surcharge_amount, income_total, pay_total, currency, ar_bill_status,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, round_mode,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, error_message, round_mode,
|
||||
create_time
|
||||
) values (
|
||||
#{billId}, #{billKey}, #{orderNo}, #{trackingNo}, #{transferNo},
|
||||
#{belongSaler}, #{belongSalerCode}, #{settlementMan}, #{settlementManCode}, #{serviceMan}, #{serviceManCode},
|
||||
#{customerId}, #{customerCode}, #{customerName},
|
||||
#{pieces}, #{actualWeight}, #{receiveWeight}, #{volume}, #{volumeWeight}, #{pkgLength}, #{pkgWidth}, #{pkgHeight},
|
||||
#{shipperName}, #{consigneeName}, #{destPostcode}, #{warehouseCode}, #{orderTime}, #{orderRemark},
|
||||
#{shipperName}, #{consigneeName}, #{destPostcode}, #{warehouseCode}, #{orderTime}, #{orderStatus}, #{blPickup}, #{pickupDate}, #{orderRemark},
|
||||
#{productCode}, #{productName}, #{countryCode}, #{countryName}, #{weight}, #{bizDate}, #{tmsId},
|
||||
#{matchSource}, #{matchMessage}, #{priceSetId}, #{priceSetCode}, #{priceSetName},
|
||||
#{setType}, #{bindPriority}, #{customerPricesetId}, #{psvId}, #{psdId}, #{productCountryId},
|
||||
#{weightMin}, #{weightMax}, #{weightLabel}, #{effectiveDate}, #{chargeWeight},
|
||||
#{freightRate}, #{baseFreightRate}, #{discountPercent}, #{registrationFee},
|
||||
#{freightAmount}, #{surchargeAmount}, #{incomeTotal}, #{payTotal}, #{currency}, #{arBillStatus},
|
||||
#{customerActiveStatus}, #{creditArrearsStatus}, #{refundStatus}, #{billingFailStatus}, #{roundMode},
|
||||
#{customerActiveStatus}, #{creditArrearsStatus}, #{refundStatus}, #{billingFailStatus}, #{errorMessage}, #{roundMode},
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
@@ -597,6 +617,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
dest_postcode = #{destPostcode},
|
||||
warehouse_code = #{warehouseCode},
|
||||
order_time = #{orderTime},
|
||||
order_status = #{orderStatus},
|
||||
bl_pickup = #{blPickup},
|
||||
pickup_date = #{pickupDate},
|
||||
order_remark = #{orderRemark},
|
||||
product_code = #{productCode},
|
||||
product_name = #{productName},
|
||||
@@ -636,6 +659,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
credit_arrears_status = #{creditArrearsStatus},
|
||||
refund_status = #{refundStatus},
|
||||
billing_fail_status = #{billingFailStatus},
|
||||
error_message = #{errorMessage},
|
||||
round_mode = #{roundMode},
|
||||
update_time = sysdate()
|
||||
where bill_id = #{billId}
|
||||
|
||||
@@ -128,7 +128,7 @@ CREATE TABLE `ar_bill_detail` (
|
||||
`total_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '订单费用合计',
|
||||
`product_amount` decimal(18,4) DEFAULT NULL COMMENT '产品价合计快照',
|
||||
`redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价合计快照',
|
||||
`billing_fail_status` tinyint DEFAULT NULL COMMENT '计费失败状态快照:0成功/1未匹配产品/2未匹配国家/3未匹配重量段',
|
||||
`billing_fail_status` tinyint DEFAULT NULL COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败。仅0/1/2/3写入应收主表,仅0写入应收费用',
|
||||
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单账单备注',
|
||||
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
@@ -942,6 +942,7 @@ CREATE TABLE `prc_income_bill` (
|
||||
`redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价合计',
|
||||
`sales_amount` decimal(18,4) DEFAULT NULL COMMENT '销售价合计(当前计费结果)',
|
||||
`income_total` decimal(18,4) DEFAULT NULL COMMENT '应收合计(当前等于销售价)',
|
||||
`refund_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE' COMMENT '是否退货:NONE未退货/REFUNDED已退货',
|
||||
`belong_saler` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员/销售员',
|
||||
`belong_saler_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '销售员编码',
|
||||
`settlement_man` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员',
|
||||
@@ -1036,11 +1037,11 @@ CREATE TABLE `prc_order_bill` (
|
||||
`warehouse_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '仓库',
|
||||
`order_time` datetime DEFAULT NULL COMMENT '下单时间',
|
||||
`order_remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单备注',
|
||||
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码',
|
||||
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品代码',
|
||||
`product_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称',
|
||||
`country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '国家二字码',
|
||||
`country_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '国家名称',
|
||||
`weight` decimal(10,3) NOT NULL COMMENT '计费重量KG',
|
||||
`weight` decimal(10,3) DEFAULT NULL COMMENT '计费重量KG',
|
||||
`biz_date` datetime NOT NULL COMMENT '计费时间',
|
||||
`tms_id` bigint(20) DEFAULT NULL COMMENT '多租户系统ID',
|
||||
`match_source` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '命中来源',
|
||||
@@ -1072,7 +1073,11 @@ CREATE TABLE `prc_order_bill` (
|
||||
`customer_active_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户生效状态:ACTIVE正常 MISSING不存在 DISABLED停用',
|
||||
`credit_arrears_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '额度欠费状态:NORMAL正常 ARREARS欠费',
|
||||
`refund_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE' COMMENT '退费状态:NONE未退费/REFUNDED已退费',
|
||||
`billing_fail_status` tinyint NOT NULL DEFAULT '0' COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段',
|
||||
`order_status` int(11) DEFAULT NULL COMMENT '上游订单状态',
|
||||
`bl_pickup` tinyint(4) DEFAULT NULL COMMENT '是否收货:0否/1是',
|
||||
`pickup_date` datetime DEFAULT NULL COMMENT '收货时间',
|
||||
`error_message` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因',
|
||||
`billing_fail_status` tinyint NOT NULL DEFAULT '0' COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败。仅0/1/2/3写入应收主表,仅0写入应收费用',
|
||||
`round_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式',
|
||||
`created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
-- Move order source back under system monitor, add read-only order bill query under billing center
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @monitorParent := (
|
||||
SELECT menu_id FROM sys_menu
|
||||
WHERE menu_type = 'M' AND (path = 'monitor' OR menu_name = '系统监控')
|
||||
ORDER BY menu_id
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET parent_id = @monitorParent,
|
||||
menu_name = '订单数据源',
|
||||
order_num = 9,
|
||||
path = 'orderSource',
|
||||
component = 'monitor/orderSource/index',
|
||||
route_name = 'OrderSource',
|
||||
visible = '0',
|
||||
status = '0',
|
||||
update_by = 'admin',
|
||||
update_time = sysdate()
|
||||
WHERE @monitorParent IS NOT NULL
|
||||
AND menu_type = 'C'
|
||||
AND (menu_id = 2325 OR perms = 'monitor:orderSource:list');
|
||||
|
||||
SET @billParent := (
|
||||
SELECT parent_id FROM sys_menu
|
||||
WHERE menu_type = 'C' AND component = 'price/billing/bill'
|
||||
ORDER BY menu_id
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
SET @orderBillMenuExists := (
|
||||
SELECT COUNT(*) FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill:list'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET order_num = order_num + 1
|
||||
WHERE @billParent IS NOT NULL
|
||||
AND @orderBillMenuExists = 0
|
||||
AND parent_id = @billParent
|
||||
AND menu_type = 'C';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component, query, route_name,
|
||||
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark
|
||||
)
|
||||
SELECT 2340, '订单列表查询', @billParent, 1,
|
||||
'orderBill', 'price/orderBill/index', '', 'OrderBillQuery',
|
||||
1, 0, 'C', '0', '0', 'price:orderBill:list', 'list', 'admin', sysdate(),
|
||||
'查询 prc_order_bill 全部订单,只读'
|
||||
FROM DUAL
|
||||
WHERE @billParent IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill:list');
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time
|
||||
)
|
||||
SELECT 2341, '查询', 2340, 1, '', NULL,
|
||||
1, 0, 'F', '0', '0', 'price:orderBill:list', '#', 'admin', sysdate()
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2341);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, m.menu_id
|
||||
FROM (SELECT 2340 AS menu_id UNION SELECT 2341) m
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, 2340
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_menu src ON src.menu_id = rm.menu_id
|
||||
WHERE src.menu_type = 'C' AND src.component = 'price/billing/bill'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2340
|
||||
);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, 2341
|
||||
FROM sys_role_menu rm
|
||||
WHERE rm.menu_id = 2340
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2341
|
||||
);
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Order list query: rebill button, same upstream sync as order data source
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark
|
||||
)
|
||||
SELECT 2342, '重新计费', 2340, 2, '', NULL,
|
||||
1, 0, 'F', '0', '0', 'price:orderBill:rebill', '#', 'admin', sysdate(),
|
||||
'订单列表按单号或选中行重走上游计费'
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2342 OR perms = 'price:orderBill:rebill');
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, 2342
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu WHERE role_id = 1 AND menu_id = 2342);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, 2342
|
||||
FROM sys_role_menu rm
|
||||
WHERE rm.menu_id = 2340
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2342
|
||||
);
|
||||
@@ -0,0 +1,52 @@
|
||||
-- idempotent: order bill fail snapshot + income refund_status
|
||||
SET @db = DATABASE();
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE prc_order_bill ADD COLUMN error_message varchar(1000) NULL COMMENT \'失败原因\' AFTER billing_fail_status',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'error_message'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE prc_order_bill ADD COLUMN order_status int NULL COMMENT \'上游订单状态\' AFTER order_time',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'order_status'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE prc_order_bill ADD COLUMN bl_pickup tinyint NULL COMMENT \'是否收货:0否/1是\' AFTER order_status',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'bl_pickup'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE prc_order_bill ADD COLUMN pickup_date datetime NULL COMMENT \'收货时间\' AFTER bl_pickup',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'pickup_date'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE prc_income_bill ADD COLUMN refund_status varchar(20) NOT NULL DEFAULT \'NONE\' COMMENT \'是否退货:NONE未退货/REFUNDED已退货\' AFTER income_total',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_income_bill' AND COLUMN_NAME = 'refund_status'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE prc_order_bill
|
||||
MODIFY COLUMN product_code varchar(20) NULL COMMENT '产品代码',
|
||||
MODIFY COLUMN weight decimal(10,3) NULL COMMENT '计费重量KG',
|
||||
MODIFY COLUMN billing_fail_status tinyint NOT NULL DEFAULT 0 COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败。仅0/1/2/3写入应收主表,仅0写入应收费用';
|
||||
@@ -57,6 +57,14 @@ export function listBill(query) {
|
||||
})
|
||||
}
|
||||
|
||||
export function listOrderBill(query) {
|
||||
return request({
|
||||
url: '/price/billing/orderBill/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** Receivable bill detail */
|
||||
export function getBill(billId) {
|
||||
return request({
|
||||
|
||||
@@ -40,8 +40,31 @@
|
||||
/>
|
||||
</el-row>
|
||||
|
||||
<!-- 虚拟滚动:只渲染可视行 -->
|
||||
<div
|
||||
v-if="virtual"
|
||||
ref="virtualHostRef"
|
||||
class="basic-table-virtual"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-v2
|
||||
v-if="virtualWidth > 0 && tableData.length"
|
||||
:columns="virtualColumns"
|
||||
:data="tableData"
|
||||
:width="virtualWidth"
|
||||
:height="virtualHeight"
|
||||
:row-height="32"
|
||||
:header-height="32"
|
||||
:row-key="virtualRowKey"
|
||||
fixed
|
||||
:row-event-handlers="virtualRowHandlers"
|
||||
/>
|
||||
<el-empty v-else-if="!loading" />
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
v-else
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
@@ -58,7 +81,7 @@
|
||||
@sort-change="(c) => $emit('sort-change', c)"
|
||||
>
|
||||
<!-- 多选列 -->
|
||||
<el-table-column v-if="selection" type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="selection" type="selection" width="55" align="center" :fixed="leftFixed ? 'left' : false" />
|
||||
|
||||
<!-- 序号列 -->
|
||||
<el-table-column v-if="showIndex" type="index" label="序号" width="55" align="center" />
|
||||
@@ -116,6 +139,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElCheckbox, ElTooltip, TableV2Alignment, TableV2FixedDir } from 'element-plus'
|
||||
/**
|
||||
* 公共表格组件 BasicTable
|
||||
* 集成搜索栏 + 工具栏 + 表格 + 分页,基于 columns/searchColumns 配置驱动,内置复用 BasicForm、RightToolbar、Pagination、DictTag。
|
||||
@@ -283,6 +307,11 @@ const props = defineProps({
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// Render only visible rows. Needs a bounded height; falls back to the viewport.
|
||||
virtual: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -327,6 +356,7 @@ watch(
|
||||
|
||||
// 实际渲染的列(visible !== false)
|
||||
const visibleColumns = computed(() => tableColumns.value.filter((c) => c.visible !== false))
|
||||
const leftFixed = computed(() => visibleColumns.value.some((c) => c.fixed === 'left' || c.fixed === true))
|
||||
|
||||
// 参与显隐工具栏的列(有 label 且未禁用工具栏显示)
|
||||
const toolbarColumns = computed(() =>
|
||||
@@ -375,6 +405,7 @@ async function getList() {
|
||||
const res = await props.query(params)
|
||||
tableData.value = res?.rows || []
|
||||
total.value = res?.total || 0
|
||||
if (props.virtual) selectionList.value = []
|
||||
} catch (e) {
|
||||
console.error('BasicTable 加载失败', e)
|
||||
// 查询失败时清空旧结果,避免用户误以为旧列表是本次查询结果。
|
||||
@@ -426,9 +457,17 @@ function getSelectionIds() {
|
||||
return selectionIds.value
|
||||
}
|
||||
function clearSelection() {
|
||||
if (props.virtual) {
|
||||
handleSelectionChange([])
|
||||
return
|
||||
}
|
||||
tableRef.value?.clearSelection()
|
||||
}
|
||||
function toggleRowSelection(row, selected) {
|
||||
if (props.virtual) {
|
||||
toggleVirtualRow(row, selected !== false)
|
||||
return
|
||||
}
|
||||
tableRef.value?.toggleRowSelection(row, selected)
|
||||
}
|
||||
function getTableRef() {
|
||||
@@ -463,13 +502,257 @@ defineExpose({
|
||||
data: tableData
|
||||
})
|
||||
|
||||
const virtualHostRef = ref()
|
||||
const virtualWidth = ref(0)
|
||||
const virtualHeight = ref(480)
|
||||
let virtualResizeObserver = null
|
||||
|
||||
function measureVirtual() {
|
||||
const host = virtualHostRef.value
|
||||
if (!host) return
|
||||
virtualWidth.value = Math.max(0, Math.floor(host.clientWidth))
|
||||
const passed = Number(props.height)
|
||||
if (Number.isFinite(passed) && passed > 0) {
|
||||
virtualHeight.value = passed
|
||||
return
|
||||
}
|
||||
const top = host.getBoundingClientRect().top
|
||||
virtualHeight.value = Math.max(240, Math.floor(window.innerHeight - top - 70))
|
||||
}
|
||||
|
||||
function virtualRowId(row) {
|
||||
if (!row) return undefined
|
||||
if (typeof props.rowKey === 'function') return props.rowKey(row)
|
||||
if (props.rowKey) return row[props.rowKey]
|
||||
return row.id
|
||||
}
|
||||
|
||||
const virtualRowKey = computed(() => (typeof props.rowKey === 'string' && props.rowKey) ? props.rowKey : 'id')
|
||||
|
||||
function isRowSelected(row) {
|
||||
const key = virtualRowId(row)
|
||||
return selectionList.value.some((item) => virtualRowId(item) === key)
|
||||
}
|
||||
|
||||
function allRowsSelected() {
|
||||
return tableData.value.length > 0 && tableData.value.every((row) => isRowSelected(row))
|
||||
}
|
||||
|
||||
function someRowsSelected() {
|
||||
return selectionList.value.length > 0 && !allRowsSelected()
|
||||
}
|
||||
|
||||
function toggleVirtualRow(row, checked) {
|
||||
const key = virtualRowId(row)
|
||||
const next = selectionList.value.filter((item) => virtualRowId(item) !== key)
|
||||
if (checked) next.push(row)
|
||||
handleSelectionChange(next)
|
||||
}
|
||||
|
||||
function toggleVirtualAll(checked) {
|
||||
handleSelectionChange(checked ? tableData.value.slice() : [])
|
||||
}
|
||||
|
||||
function renderVirtualCheckbox(checked, indeterminate, onChange) {
|
||||
return h(ElCheckbox, {
|
||||
modelValue: checked,
|
||||
indeterminate,
|
||||
'onUpdate:modelValue': onChange,
|
||||
onClick: (event) => event.stopPropagation()
|
||||
})
|
||||
}
|
||||
|
||||
function columnWidth(col) {
|
||||
const width = Number(col.width || col.minWidth)
|
||||
return Number.isFinite(width) && width > 0 ? width : 140
|
||||
}
|
||||
|
||||
function columnAlign() {
|
||||
return TableV2Alignment.LEFT
|
||||
}
|
||||
|
||||
function columnFixed(col) {
|
||||
if (col.fixed === 'left' || col.fixed === true) return TableV2FixedDir.LEFT
|
||||
if (col.fixed === 'right') return TableV2FixedDir.RIGHT
|
||||
return undefined
|
||||
}
|
||||
|
||||
function renderVirtualText(col, row, rowIndex) {
|
||||
let text = row ? row[col.prop] : ''
|
||||
if (col.parseTime) {
|
||||
text = proxy.parseTime(text, col.parseTime === true ? undefined : col.parseTime)
|
||||
} else if (col.formatter) {
|
||||
text = col.formatter(row, col, text, rowIndex)
|
||||
}
|
||||
if (text == null || text === '') return ''
|
||||
return String(text)
|
||||
}
|
||||
|
||||
const virtualColumns = computed(() => {
|
||||
const cols = []
|
||||
if (props.selection) {
|
||||
cols.push({
|
||||
key: '__selection',
|
||||
dataKey: '__selection',
|
||||
width: 55,
|
||||
align: TableV2Alignment.CENTER,
|
||||
fixed: leftFixed.value ? TableV2FixedDir.LEFT : undefined,
|
||||
headerCellRenderer: () => renderVirtualCheckbox(allRowsSelected(), someRowsSelected(), toggleVirtualAll),
|
||||
cellRenderer: ({ rowData }) => renderVirtualCheckbox(isRowSelected(rowData), false, (checked) => toggleVirtualRow(rowData, checked))
|
||||
})
|
||||
}
|
||||
if (props.showIndex) {
|
||||
cols.push({
|
||||
key: '__index',
|
||||
dataKey: '__index',
|
||||
title: '#',
|
||||
width: 55,
|
||||
align: TableV2Alignment.CENTER,
|
||||
cellRenderer: ({ rowIndex }) => String((queryParams.pageNum - 1) * queryParams.pageSize + rowIndex + 1)
|
||||
})
|
||||
}
|
||||
visibleColumns.value.forEach((col) => {
|
||||
const slotFn = slots[`col-${col.prop}`]
|
||||
const headerSlot = slots[`header-${col.prop}`]
|
||||
const column = {
|
||||
key: String(col.key || col.prop),
|
||||
dataKey: col.prop,
|
||||
title: col.label,
|
||||
width: columnWidth(col),
|
||||
align: columnAlign(col),
|
||||
fixed: columnFixed(col),
|
||||
cellRenderer: ({ rowData, rowIndex }) => {
|
||||
if (col.dict) {
|
||||
return h(resolveComponent('DictTag'), {
|
||||
options: getDictOptions(col.dict),
|
||||
value: rowData[col.prop]
|
||||
})
|
||||
}
|
||||
if (slotFn) {
|
||||
return h('div', { class: 'basic-table-vcell' }, slotFn({ row: rowData, $index: rowIndex }))
|
||||
}
|
||||
const text = renderVirtualText(col, rowData, rowIndex)
|
||||
if (!text) return h('div', { class: 'basic-table-vcell' }, '')
|
||||
return h(ElTooltip, {
|
||||
content: text,
|
||||
placement: 'top',
|
||||
showAfter: 300,
|
||||
enterable: false
|
||||
}, {
|
||||
default: () => h('div', { class: 'basic-table-vcell' }, text)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (headerSlot) column.headerCellRenderer = () => headerSlot({ column: col })
|
||||
cols.push(column)
|
||||
})
|
||||
return cols
|
||||
})
|
||||
|
||||
const virtualRowHandlers = {
|
||||
onClick({ rowData, event }) {
|
||||
emit('row-click', rowData, null, event)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.height, () => nextTick(measureVirtual))
|
||||
|
||||
if (props.autoLoad) {
|
||||
onMounted(getList)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!props.virtual) return
|
||||
nextTick(() => {
|
||||
measureVirtual()
|
||||
if (virtualHostRef.value && typeof ResizeObserver !== 'undefined') {
|
||||
virtualResizeObserver = new ResizeObserver(() => measureVirtual())
|
||||
virtualResizeObserver.observe(virtualHostRef.value)
|
||||
}
|
||||
window.addEventListener('resize', measureVirtual)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (virtualResizeObserver) virtualResizeObserver.disconnect()
|
||||
window.removeEventListener('resize', measureVirtual)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.basic-table {
|
||||
width: 100%;
|
||||
}
|
||||
.basic-table-virtual {
|
||||
width: 100%;
|
||||
min-height: 240px;
|
||||
border: 1px solid #ebeef5;
|
||||
background: #fff;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2) {
|
||||
--el-table-header-bg-color: #f8f8f9;
|
||||
--el-table-header-text-color: #515a6e;
|
||||
--el-table-border-color: #ebeef5;
|
||||
--el-table-row-hover-bg-color: #f5f7fa;
|
||||
--el-table-text-color: #606266;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__header-row) {
|
||||
background: #f8f8f9;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__header-cell) {
|
||||
background: #f8f8f9;
|
||||
color: #515a6e;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
border-right: 1px solid #ebeef5;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
justify-content: flex-start !important;
|
||||
text-align: left;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__header-cell-text) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__header-cell:last-child),
|
||||
.basic-table-virtual :deep(.el-table-v2__row-cell:last-child) {
|
||||
border-right: none;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__row) {
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__row:hover) {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__row-cell) {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
padding: 0 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
min-width: 0;
|
||||
}
|
||||
.basic-table-virtual :deep(.el-table-v2__row-cell .el-tooltip__trigger) {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.basic-table-vcell {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
line-height: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
:search-per-row="4"
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
:page-size="100"
|
||||
virtual
|
||||
>
|
||||
<template #toolbar="{ selection }">
|
||||
<el-col :span="1.5">
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
search-action-align="center"
|
||||
:search-per-row="4"
|
||||
row-key="billId"
|
||||
selection
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
:page-size="100"
|
||||
:height="tableHeight"
|
||||
virtual
|
||||
>
|
||||
<template #search-orderNos="{ model }">
|
||||
<multi-value-input v-model="model.orderNos" placeholder="订单号" width="100%" :search-method="searchOrderNoOptions" />
|
||||
</template>
|
||||
<template #search-trackingNos="{ model }">
|
||||
<multi-value-input v-model="model.trackingNos" placeholder="运单号" width="100%" :search-method="searchTrackingNoOptions" />
|
||||
</template>
|
||||
<template #search-transferNos="{ model }">
|
||||
<multi-value-input v-model="model.transferNos" placeholder="转单号" width="100%" :search-method="searchTransferNoOptions" />
|
||||
<template #search-orderIdentifiers="{ model }">
|
||||
<multi-value-input
|
||||
v-model="model.orderIdentifiers"
|
||||
placeholder="订单号/运单号/转单号"
|
||||
width="100%"
|
||||
:search-method="searchOrderIdentifiers"
|
||||
/>
|
||||
</template>
|
||||
<template #search-customerCodes="{ model }">
|
||||
<customer-select
|
||||
@@ -28,8 +28,9 @@
|
||||
value-key="customerCode"
|
||||
label-key="customerName"
|
||||
code-key="customerCode"
|
||||
placeholder="请输入"
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
/>
|
||||
</template>
|
||||
<template #search-productCodes="{ model }">
|
||||
@@ -232,19 +233,13 @@ import EditChargeWeightDialog from './components/EditChargeWeightDialog.vue'
|
||||
import ExcelImportDialog from '@/components/ExcelImportDialog'
|
||||
import ChargeWeightImportAttachDrawer from './components/ChargeWeightImportAttachDrawer.vue'
|
||||
|
||||
// 订单号下拉远程搜索:返回匹配的订单号列表(最多 20 条)
|
||||
function searchOrderNoOptions(keyword) {
|
||||
return listOrderNoOptions(keyword).then(res => res.data || [])
|
||||
}
|
||||
|
||||
// 运单号下拉远程搜索
|
||||
function searchTrackingNoOptions(keyword) {
|
||||
return listTrackingNoOptions(keyword).then(res => res.data || [])
|
||||
}
|
||||
|
||||
// 转单号下拉远程搜索
|
||||
function searchTransferNoOptions(keyword) {
|
||||
return listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
// 单号下拉:订单号、运单号、转单号合并候选
|
||||
function searchOrderIdentifiers(keyword) {
|
||||
return Promise.all([
|
||||
listOrderNoOptions(keyword).then(res => res.data || []),
|
||||
listTrackingNoOptions(keyword).then(res => res.data || []),
|
||||
listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
]).then(groups => [...new Set(groups.flat().map(v => String(v)))])
|
||||
}
|
||||
|
||||
// 产品下拉选项:复用 ProductSelect 的模块级产品缓存(全应用只拉一次)
|
||||
@@ -257,6 +252,37 @@ const productOptions = computed(() =>
|
||||
)
|
||||
|
||||
const tableRef = ref()
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
function updateTableHeight() {
|
||||
const tableEl = document.querySelector('.billing-bill > .basic-table .el-table, .billing-bill > .basic-table .basic-table-virtual')
|
||||
if (!tableEl) return
|
||||
const top = tableEl.getBoundingClientRect().top
|
||||
const paginationReserve = 58
|
||||
const bottomGap = 12
|
||||
tableHeight.value = Math.max(240, Math.floor(window.innerHeight - top - paginationReserve - bottomGap))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateTableHeight()
|
||||
const form = document.querySelector('.billing-bill > .basic-table .basic-form')
|
||||
if (form && typeof ResizeObserver !== 'undefined') {
|
||||
tableResizeObserver = new ResizeObserver(() => updateTableHeight())
|
||||
tableResizeObserver.observe(form)
|
||||
}
|
||||
})
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
if (tableResizeObserver) {
|
||||
tableResizeObserver.disconnect()
|
||||
tableResizeObserver = null
|
||||
}
|
||||
})
|
||||
const open = ref(false)
|
||||
const title = ref('计费详情')
|
||||
const loading = ref(false)
|
||||
@@ -286,7 +312,7 @@ const recalcDialogOpen = ref(false)
|
||||
const recalcMode = ref('PRODUCT')
|
||||
|
||||
const columns = [
|
||||
{ label: '订单号', prop: 'orderNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '订单号', prop: 'orderNo', width: 160, fixed: 'left', showOverflowTooltip: true },
|
||||
{ label: '运单号', prop: 'trackingNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '转单号', prop: 'transferNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户', prop: 'customerName', minWidth: 140, showOverflowTooltip: true },
|
||||
@@ -320,19 +346,16 @@ const refundStatusOptions = [
|
||||
{ label: '未退费', value: 'NONE' },
|
||||
{ label: '已退费', value: 'REFUNDED' }
|
||||
]
|
||||
/** billing_fail_status: 0 success / 1 product / 2 country / 3 weight */
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '\u8ba1\u8d39\u6210\u529f', value: 0 },
|
||||
{ label: '\u672a\u5339\u914d\u4ea7\u54c1', value: 1 },
|
||||
{ label: '\u672a\u5339\u914d\u56fd\u5bb6', value: 2 },
|
||||
{ label: '\u672a\u5339\u914d\u91cd\u91cf\u6bb5', value: 3 }
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '订单号', prop: 'orderNos', placeholder: '订单号' },
|
||||
{ label: '运单号', prop: 'trackingNos', placeholder: '运单号' },
|
||||
{ label: '转单号', prop: 'transferNos', placeholder: '转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户代码' },
|
||||
{ label: '单号', prop: 'orderIdentifiers', placeholder: '订单号/运单号/转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户名称/客户代码/对外代码' },
|
||||
{ label: '产品代码', prop: 'productCodes', placeholder: '产品代码' },
|
||||
{
|
||||
label: '额度欠费',
|
||||
@@ -443,7 +466,8 @@ function onDetailUpdated(data) {
|
||||
|
||||
function hasBatchFeeQuery(q) {
|
||||
const hasArr = (arr) => Array.isArray(arr) && arr.some(v => v != null && String(v).trim() !== '')
|
||||
return hasArr(q.orderNos)
|
||||
return hasArr(q.orderIdentifiers)
|
||||
|| hasArr(q.orderNos)
|
||||
|| hasArr(q.trackingNos)
|
||||
|| hasArr(q.transferNos)
|
||||
|| hasArr(q.customerCodes)
|
||||
@@ -488,6 +512,7 @@ function buildRecalcPayload() {
|
||||
orderNos: rest.orderNos || [],
|
||||
trackingNos: rest.trackingNos || [],
|
||||
transferNos: rest.transferNos || [],
|
||||
orderIdentifiers: rest.orderIdentifiers || [],
|
||||
customerCodes: rest.customerCodes || [],
|
||||
productCodes: rest.productCodes || [],
|
||||
creditArrearsStatus: rest.creditArrearsStatus || undefined
|
||||
@@ -578,6 +603,7 @@ function buildChargeWeightTemplateQuery() {
|
||||
orderNos: rest.orderNos || [],
|
||||
trackingNos: rest.trackingNos || [],
|
||||
transferNos: rest.transferNos || [],
|
||||
orderIdentifiers: rest.orderIdentifiers || [],
|
||||
customerCodes: rest.customerCodes || [],
|
||||
productCodes: rest.productCodes || [],
|
||||
creditArrearsStatus: rest.creditArrearsStatus || undefined
|
||||
@@ -691,15 +717,13 @@ function formatWeight(val) {
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
if (code > 0) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,8 @@ function fmtList(arr, max = 5) {
|
||||
|
||||
function buildQueryTags(q) {
|
||||
const tags = []
|
||||
const identifiers = fmtList(q.orderIdentifiers)
|
||||
if (identifiers) tags.push("单号" + ':' + identifiers)
|
||||
const order = fmtList(q.orderNos)
|
||||
if (order) tags.push("订单号" + ':' + order)
|
||||
const tracking = fmtList(q.trackingNos)
|
||||
@@ -256,6 +258,7 @@ function buildPayload() {
|
||||
orderNos: q.orderNos || [],
|
||||
trackingNos: q.trackingNos || [],
|
||||
transferNos: q.transferNos || [],
|
||||
orderIdentifiers: q.orderIdentifiers || [],
|
||||
customerCodes: q.customerCodes || [],
|
||||
productCodes: q.productCodes || [],
|
||||
creditArrearsStatus: q.creditArrearsStatus || undefined,
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<div class="sec-title">费用明细</div>
|
||||
<el-tabs v-model="feeTab" class="fee-tabs">
|
||||
<el-tab-pane label="应收" name="income">
|
||||
<div class="fee-toolbar">
|
||||
<div v-if="!readonly" class="fee-toolbar">
|
||||
<el-button type="primary" plain size="small" icon="Plus" v-hasPermi="['price:billing:feeAdd']" @click="openAddFee">增加费用</el-button>
|
||||
<el-button type="primary" plain size="small" icon="Edit" v-hasPermi="['price:billing:chargeWeightEdit']" :disabled="detail.refundStatus === 'REFUNDED'" @click="openEditCw">修改计费重</el-button>
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@
|
||||
<el-table-column label="说明" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.remark || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<el-table-column v-if="!readonly" label="操作" width="120" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="canEditManualFee(row)">
|
||||
<el-button link type="primary" v-hasPermi="['price:billing:feeEdit']" @click="openEditFee(row)">编辑</el-button>
|
||||
@@ -198,7 +198,8 @@ const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: '计费详情' },
|
||||
loading: { type: Boolean, default: false },
|
||||
detail: { type: Object, default: null }
|
||||
detail: { type: Object, default: null },
|
||||
readonly: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'updated'])
|
||||
@@ -335,17 +336,41 @@ function formatDateTime(val) {
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
|
||||
}
|
||||
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
{ label: '未绑定套餐', value: 4 },
|
||||
{ label: '无生效套餐版本', value: 5 },
|
||||
{ label: '已生成应收账单', value: 6 },
|
||||
{ label: '产品档案缺失', value: 7 },
|
||||
{ label: '未命中价格', value: 8 },
|
||||
{ label: '分区无价格', value: 9 },
|
||||
{ label: '邮编必填', value: 10 },
|
||||
{ label: '邮编未匹配分区', value: 11 },
|
||||
{ label: '上游订单不存在', value: 12 },
|
||||
{ label: '上游订单号为空', value: 13 },
|
||||
{ label: '上游客户为空', value: 14 },
|
||||
{ label: '上游产品为空', value: 15 },
|
||||
{ label: '上游重量无效', value: 16 },
|
||||
{ label: '计费时间为空', value: 17 },
|
||||
{ label: '退费订单无法解析', value: 18 },
|
||||
{ label: '客户代码为空', value: 19 },
|
||||
{ label: '客户已停用', value: 20 },
|
||||
{ label: '运费为空', value: 21 },
|
||||
{ label: '未知失败', value: 22 }
|
||||
]
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
if (code > 0) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<div class="app-container order-bill-query">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
:search-per-row="4"
|
||||
row-key="billId"
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
:page-size="100"
|
||||
selection
|
||||
:height="tableHeight"
|
||||
virtual
|
||||
>
|
||||
<template #toolbar="{ selection }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Refresh"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="openFilterRebill()"
|
||||
>重新计费</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Document" @click="logDrawerRef.open()">操作记录</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Refresh"
|
||||
:disabled="!selection.length"
|
||||
:loading="batchLoading"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="handleBatchRebill(selection)"
|
||||
>重新计费选中</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #search-orderIdentifiers="{ model }">
|
||||
<multi-value-input
|
||||
v-model="model.orderIdentifiers"
|
||||
placeholder="订单号/运单号/转单号"
|
||||
width="100%"
|
||||
:search-method="searchOrderIdentifiers"
|
||||
/>
|
||||
</template>
|
||||
<template #search-customerCodes="{ model }">
|
||||
<customer-select
|
||||
v-model="model.customerCodes"
|
||||
type="customer"
|
||||
value-key="customerCode"
|
||||
label-key="customerName"
|
||||
code-key="customerCode"
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
/>
|
||||
</template>
|
||||
<template #col-product="{ row }">
|
||||
{{ row.productName || '-' }} ({{ row.productCode || '-' }})
|
||||
</template>
|
||||
<template #col-country="{ row }">
|
||||
{{ row.countryName || '-' }} ({{ row.countryCode || '-' }})
|
||||
</template>
|
||||
<template #col-weight="{ row }">
|
||||
{{ formatWeight(row.weight) }}
|
||||
</template>
|
||||
<template #col-volumeWeight="{ row }">
|
||||
{{ formatWeight(row.volumeWeight) }}
|
||||
</template>
|
||||
<template #col-receiveWeight="{ row }">
|
||||
{{ formatWeight(row.receiveWeight) }}
|
||||
</template>
|
||||
<template #col-orderStatus="{ row }">
|
||||
{{ orderStatusLabel(row.orderStatus) }}
|
||||
</template>
|
||||
<template #col-billingFailStatus="{ row }">
|
||||
<el-tag size="small" :type="billingFailTagType(row.billingFailStatus)">{{ billingFailLabel(row.billingFailStatus) }}</el-tag>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="Refresh"
|
||||
:loading="rebillingKey === row.billId"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="handleRebillOne(row)"
|
||||
>重新计费</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
<order-source-sync-dialog ref="syncDialogRef" @success="onRebillSuccess" />
|
||||
<order-source-sync-log-drawer ref="logDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OrderBillQuery">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { listOrderBill, listOrderNoOptions, listTrackingNoOptions, listTransferNoOptions } from '@/api/price/billing'
|
||||
import { syncOrderSourceSelected } from '@/api/monitor/orderSource'
|
||||
import OrderSourceSyncDialog from '../../monitor/orderSource/components/OrderSourceSyncDialog.vue'
|
||||
import OrderSourceSyncLogDrawer from '../../monitor/orderSource/components/OrderSourceSyncLogDrawer.vue'
|
||||
|
||||
const tableRef = ref()
|
||||
const syncDialogRef = ref()
|
||||
const logDrawerRef = ref()
|
||||
const batchLoading = ref(false)
|
||||
const rebillingKey = ref(null)
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
const columns = [
|
||||
{ label: '订单号', prop: 'orderNo', width: 160, fixed: 'left', showOverflowTooltip: true },
|
||||
{ label: '运单号', prop: 'trackingNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '转单号', prop: 'transferNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户', prop: 'customerName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户代码', prop: 'customerCode', width: 120 },
|
||||
{ label: '产品', prop: 'product', minWidth: 160, slot: 'product' },
|
||||
{ label: '国家', prop: 'country', minWidth: 130, slot: 'country' },
|
||||
{ label: '件数', prop: 'pieces', align: 'center', width: 70 },
|
||||
{ label: '计费重', prop: 'weight', align: 'center', width: 90, slot: 'weight' },
|
||||
{ label: '体积重', prop: 'volumeWeight', align: 'center', width: 90, slot: 'volumeWeight' },
|
||||
{ label: '实重', prop: 'receiveWeight', align: 'center', width: 90, slot: 'receiveWeight' },
|
||||
{ label: '收件人邮编', prop: 'destPostcode', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '订单状态', prop: 'orderStatus', align: 'center', width: 110, slot: 'orderStatus' },
|
||||
{ label: '计费状态', prop: 'billingFailStatus', align: 'center', width: 140, slot: 'billingFailStatus' },
|
||||
{ label: '计费时间', prop: 'bizDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '下单时间', prop: 'orderTime', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '提货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 120, fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
]
|
||||
|
||||
const orderStatusMap = {
|
||||
0: '预报订单',
|
||||
1: '确认订单',
|
||||
2: '已中转',
|
||||
3: '已收货',
|
||||
4: '已装箱',
|
||||
5: '已出库',
|
||||
6: '转运中',
|
||||
7: '已上网',
|
||||
8: '已签收',
|
||||
9: '已退件',
|
||||
10: '已删除'
|
||||
}
|
||||
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
{ label: '未绑定套餐', value: 4 },
|
||||
{ label: '无生效套餐版本', value: 5 },
|
||||
{ label: '已生成应收账单', value: 6 },
|
||||
{ label: '产品档案缺失', value: 7 },
|
||||
{ label: '未命中价格', value: 8 },
|
||||
{ label: '分区无价格', value: 9 },
|
||||
{ label: '邮编必填', value: 10 },
|
||||
{ label: '邮编未匹配分区', value: 11 },
|
||||
{ label: '上游订单不存在', value: 12 },
|
||||
{ label: '上游订单号为空', value: 13 },
|
||||
{ label: '上游客户为空', value: 14 },
|
||||
{ label: '上游产品为空', value: 15 },
|
||||
{ label: '上游重量无效', value: 16 },
|
||||
{ label: '计费时间为空', value: 17 },
|
||||
{ label: '退费订单无法解析', value: 18 },
|
||||
{ label: '客户代码为空', value: 19 },
|
||||
{ label: '客户已停用', value: 20 },
|
||||
{ label: '运费为空', value: 21 },
|
||||
{ label: '未知失败', value: 22 }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '单号', prop: 'orderIdentifiers', placeholder: '订单号/运单号/转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户名称/客户代码/对外代码' },
|
||||
{
|
||||
label: '计费时间',
|
||||
prop: 'bizDateRange',
|
||||
component: 'datetimerange',
|
||||
colSpan: 2,
|
||||
props: {
|
||||
clearable: true,
|
||||
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '计费状态',
|
||||
prop: 'billingFailStatuses',
|
||||
component: 'select',
|
||||
options: billingFailStatusOptions,
|
||||
placeholder: '请选择'
|
||||
}
|
||||
]
|
||||
|
||||
function queryList(params) {
|
||||
const { bizDateRange, ...rest } = params
|
||||
const query = { ...rest }
|
||||
if (Array.isArray(bizDateRange) && (bizDateRange[0] || bizDateRange[1])) {
|
||||
query.params = {
|
||||
...(query.params || {}),
|
||||
beginBizDate: bizDateRange[0] || null,
|
||||
endBizDate: bizDateRange[1] || null
|
||||
}
|
||||
}
|
||||
return listOrderBill(query)
|
||||
}
|
||||
|
||||
|
||||
const MAX_REBILL = 10000
|
||||
|
||||
function currentIdentifiers() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
return (Array.isArray(q.orderIdentifiers) ? q.orderIdentifiers : [])
|
||||
.map(v => String(v || '').trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function buildRebillFilter() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
const filter = { orderIdentifiers: currentIdentifiers() }
|
||||
if (Array.isArray(q.customerCodes) && q.customerCodes.length) {
|
||||
filter.customerCodes = q.customerCodes
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
function openFilterRebill() {
|
||||
const identifiers = currentIdentifiers()
|
||||
if (!identifiers.length) {
|
||||
ElMessage.warning('请输入单号后再重新计费,或勾选订单后点重新计费选中')
|
||||
return
|
||||
}
|
||||
if (identifiers.length > MAX_REBILL) {
|
||||
ElMessage.warning('单次最多重新计费 ' + MAX_REBILL + ' 个单号')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
'将按 ' + identifiers.length + ' 个单号查上游订单并重新计费,确认继续?',
|
||||
'二次确认',
|
||||
{ type: 'warning' }
|
||||
).then(() => {
|
||||
syncDialogRef.value?.open(buildRebillFilter(), true)
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function toSyncRow(row) {
|
||||
return {
|
||||
orderId: row.orderNo,
|
||||
orgOrderId: row.trackingNo,
|
||||
transferOrderId: row.transferNo,
|
||||
customerCode: row.customerCode,
|
||||
pickupDate: row.bizDate || row.pickupDate || row.orderTime || null
|
||||
}
|
||||
}
|
||||
|
||||
function handleRebillOne(row) {
|
||||
if (!toSyncRow(row).pickupDate) {
|
||||
ElMessage.warning('该订单没有计费时间,无法重新计费')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm('确认重新计费该订单?', '二次确认', { type: 'warning' }).then(() => {
|
||||
rebillingKey.value = row.billId
|
||||
return syncOrderSourceSelected([toSyncRow(row)])
|
||||
}).then(res => {
|
||||
const data = (res && res.data) || {}
|
||||
if ((data.ok || 0) > 0 && !(data.fail || 0)) {
|
||||
ElMessage.success('重新计费成功')
|
||||
} else {
|
||||
const detail = data.details && data.details[0]
|
||||
ElMessage.error((detail && detail.errorMessage) || '重新计费失败')
|
||||
}
|
||||
tableRef.value?.refresh?.()
|
||||
}).catch(() => {}).finally(() => {
|
||||
rebillingKey.value = null
|
||||
})
|
||||
}
|
||||
|
||||
function handleBatchRebill(selection) {
|
||||
const rows = selection || []
|
||||
if (!rows.length) return
|
||||
const missingTime = rows.filter(row => !toSyncRow(row).pickupDate).length
|
||||
if (missingTime) {
|
||||
ElMessage.warning(missingTime + ' 条没有计费时间,无法重新计费')
|
||||
return
|
||||
}
|
||||
if (rows.length > 200) {
|
||||
ElMessage.warning('选中重新计费最多 200 条')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm('确认重新计费选中的 ' + rows.length + ' 条订单?', '二次确认', { type: 'warning' }).then(() => {
|
||||
batchLoading.value = true
|
||||
return syncOrderSourceSelected(rows.map(toSyncRow))
|
||||
}).then(res => {
|
||||
const data = (res && res.data) || {}
|
||||
ElMessage.success('完成:成功 ' + (data.ok || 0) + ' / 失败 ' + (data.fail || 0))
|
||||
tableRef.value?.refresh?.()
|
||||
}).catch(() => {}).finally(() => {
|
||||
batchLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function onRebillSuccess() {
|
||||
tableRef.value?.refresh?.()
|
||||
}
|
||||
|
||||
function searchOrderIdentifiers(keyword) {
|
||||
return Promise.all([
|
||||
listOrderNoOptions(keyword).then(res => res.data || []),
|
||||
listTrackingNoOptions(keyword).then(res => res.data || []),
|
||||
listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
]).then(groups => [...new Set(groups.flat().map(v => String(v)))])
|
||||
}
|
||||
|
||||
function formatWeight(val) {
|
||||
if (val == null || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function orderStatusLabel(status) {
|
||||
if (status === null || status === undefined || status === '') return '-'
|
||||
const key = Number(status)
|
||||
if (Object.prototype.hasOwnProperty.call(orderStatusMap, key)) return orderStatusMap[key]
|
||||
return String(status)
|
||||
}
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
return code > 0 ? 'danger' : 'success'
|
||||
}
|
||||
|
||||
function updateTableHeight() {
|
||||
const tableEl = document.querySelector('.order-bill-query > .basic-table .el-table, .order-bill-query > .basic-table .basic-table-virtual')
|
||||
if (!tableEl) return
|
||||
const top = tableEl.getBoundingClientRect().top
|
||||
tableHeight.value = Math.max(240, Math.floor(window.innerHeight - top - 58 - 12))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateTableHeight()
|
||||
const form = document.querySelector('.order-bill-query > .basic-table .basic-form')
|
||||
if (form && typeof ResizeObserver !== 'undefined') {
|
||||
tableResizeObserver = new ResizeObserver(() => updateTableHeight())
|
||||
tableResizeObserver.observe(form)
|
||||
}
|
||||
})
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
if (tableResizeObserver) {
|
||||
tableResizeObserver.disconnect()
|
||||
tableResizeObserver = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-select),
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-input),
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-date-editor) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user