优化计费逻辑

This commit is contained in:
wuweihua
2026-10-17 01:37:33 +08:00
parent 52f714c760
commit db6f5e882a
29 changed files with 1460 additions and 145 deletions
@@ -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;
@@ -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;
@@ -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),重算应收并回补余额。
*
@@ -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
@@ -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}