增加订单失败状态
This commit is contained in:
@@ -206,3 +206,6 @@ price.set.detail.duplicate.fixed=产品{0} 国家{1} 重量段{2} {3}(共{4}
|
||||
price.set.detail.duplicate.discount=产品{0}(共{1}条)
|
||||
price.set.detail.duplicate.hasZone=有分区({0})
|
||||
price.set.detail.duplicate.noZone=无分区
|
||||
price.bill.billingFail.product=计费失败:未匹配产品
|
||||
price.bill.billingFail.country=计费失败:未匹配国家
|
||||
price.bill.billingFail.weight=计费失败:未匹配重量段
|
||||
|
||||
@@ -116,6 +116,8 @@ public class PrcOrderBill extends BaseEntity
|
||||
private String creditArrearsStatus;
|
||||
/** NONE / REFUNDED */
|
||||
private String refundStatus;
|
||||
/** 计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段;软失败时金额为0且不落费用明细 */
|
||||
private Integer billingFailStatus;
|
||||
private String roundMode;
|
||||
|
||||
private List<PrcIncomeBillFee> incomeFees;
|
||||
|
||||
+20
-2
@@ -4,10 +4,8 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.bms.price.domain.PrcOrderBill;
|
||||
import lombok.Data;
|
||||
|
||||
/** Preview matched bills before async batch add fee. */
|
||||
@Data
|
||||
public class BatchAddIncomeBillFeePreview implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -15,4 +13,24 @@ public class BatchAddIncomeBillFeePreview implements Serializable
|
||||
private int matchCount;
|
||||
/** First N rows for UI preview. */
|
||||
private List<PrcOrderBill> samples = new ArrayList<PrcOrderBill>();
|
||||
|
||||
public int getMatchCount()
|
||||
{
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
public void setMatchCount(int matchCount)
|
||||
{
|
||||
this.matchCount = matchCount;
|
||||
}
|
||||
|
||||
public List<PrcOrderBill> getSamples()
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
public void setSamples(List<PrcOrderBill> samples)
|
||||
{
|
||||
this.samples = samples;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,8 @@ public class TmsPriceQuoteResult implements Serializable
|
||||
private String creditArrearsStatus;
|
||||
/** NONE / REFUNDED */
|
||||
private String refundStatus;
|
||||
/** 计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段(仅上游同步计费软失败) */
|
||||
private Integer billingFailStatus;
|
||||
private Integer pieces;
|
||||
private BigDecimal actualWeight;
|
||||
/** Receive weight from upstream TAB_ORDER.WEIGHT */
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.bms.price.enums;
|
||||
|
||||
/**
|
||||
* prc_order_bill.billing_fail_status 计费失败状态。
|
||||
* 0=成功;1=未匹配产品;2=未匹配国家;3=未匹配重量段。
|
||||
*/
|
||||
public enum BillBillingFailStatus
|
||||
{
|
||||
SUCCESS(0),
|
||||
PRODUCT(1),
|
||||
COUNTRY(2),
|
||||
WEIGHT(3);
|
||||
|
||||
private final int code;
|
||||
|
||||
BillBillingFailStatus(int code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public static BillBillingFailStatus of(Integer code)
|
||||
{
|
||||
if (code == null)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
for (BillBillingFailStatus status : values())
|
||||
{
|
||||
if (status.code == code.intValue())
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isFail(Integer code)
|
||||
{
|
||||
return code != null && code.intValue() > SUCCESS.code;
|
||||
}
|
||||
}
|
||||
@@ -104,4 +104,20 @@ public interface BmsProductCountryMapper
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("bizDate") Date bizDate);
|
||||
|
||||
/** Count catalog rows by product (+ bizDate window), ignore country/zone/weight. */
|
||||
public int countByProduct(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("bizDate") Date bizDate);
|
||||
|
||||
/**
|
||||
* Count catalog rows by product + country + zone (+ bizDate), ignore weight.
|
||||
* Used to diagnose country miss vs weight miss.
|
||||
*/
|
||||
public int countByProductCountryZone(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("zoneCode") String zoneCode,
|
||||
@Param("bizDate") Date bizDate);
|
||||
}
|
||||
|
||||
@@ -82,6 +82,22 @@ public interface PrcPriceSetDetailMapper
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName);
|
||||
|
||||
/** Count FIXED details by version + product (ignore country/zone/weight). */
|
||||
public int countByPsvProduct(
|
||||
@Param("psvId") Long psvId,
|
||||
@Param("productCode") String productCode);
|
||||
|
||||
/**
|
||||
* Count FIXED details by version + product + country + zone (ignore weight).
|
||||
* Used to diagnose country miss vs weight miss.
|
||||
*/
|
||||
public int countByPsvProductCountryZone(
|
||||
@Param("psvId") Long psvId,
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("zoneCode") String zoneCode);
|
||||
|
||||
/** DISCOUNT by product; optional psvId scopes to version */
|
||||
public PrcPriceSetDetail selectDiscountMatch(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
|
||||
+72
-21
@@ -60,6 +60,7 @@ import com.bms.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.bms.price.enums.BillCreditArrearsStatus;
|
||||
import com.bms.price.enums.BillCustomerActiveStatus;
|
||||
import com.bms.price.enums.BillRefundStatus;
|
||||
import com.bms.price.enums.BillBillingFailStatus;
|
||||
import com.bms.price.enums.TmsOrderBillMatchType;
|
||||
import com.bms.price.mapper.PrcIncomeBillFeeMapper;
|
||||
import com.bms.price.mapper.PrcIncomeBillMapper;
|
||||
@@ -1129,15 +1130,51 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
bill.setWeightLabel(result.getWeightLabel());
|
||||
bill.setEffectiveDate(result.getEffectiveDate());
|
||||
bill.setChargeWeight(resolvePersistChargeWeight(weight, result.getChargeWeight()));
|
||||
bill.setFreightRate(result.getFreightRate());
|
||||
bill.setBaseFreightRate(result.getBaseFreightRate());
|
||||
bill.setDiscountPercent(result.getDiscountPercent());
|
||||
bill.setRegistrationFee(result.getRegistrationFee());
|
||||
bill.setFreightAmount(result.getFreightAmount());
|
||||
bill.setSurchargeAmount(result.getSurchargeAmount());
|
||||
bill.setIncomeTotal(result.getPayableTotal());
|
||||
// 应付总额与产品目录价解耦:试算未产出 pay_total 时留空,不再用 product_amount 兜底
|
||||
bill.setPayTotal(result.getPayTotal());
|
||||
Integer billingFailStatus = result.getBillingFailStatus();
|
||||
if (billingFailStatus == null)
|
||||
{
|
||||
billingFailStatus = Integer.valueOf(BillBillingFailStatus.SUCCESS.getCode());
|
||||
}
|
||||
bill.setBillingFailStatus(billingFailStatus);
|
||||
boolean billingFailed = BillBillingFailStatus.isFail(billingFailStatus);
|
||||
if (billingFailed)
|
||||
{
|
||||
// Soft-fail: persist order snapshot with zero amounts; skip fee detail rows.
|
||||
bill.setFreightRate(BigDecimal.ZERO);
|
||||
bill.setBaseFreightRate(BigDecimal.ZERO);
|
||||
bill.setDiscountPercent(null);
|
||||
bill.setRegistrationFee(BigDecimal.ZERO);
|
||||
bill.setFreightAmount(BigDecimal.ZERO);
|
||||
bill.setSurchargeAmount(BigDecimal.ZERO);
|
||||
bill.setIncomeTotal(BigDecimal.ZERO);
|
||||
bill.setPayTotal(BigDecimal.ZERO);
|
||||
result.setFreightRate(BigDecimal.ZERO);
|
||||
result.setBaseFreightRate(BigDecimal.ZERO);
|
||||
result.setDiscountPercent(null);
|
||||
result.setRegistrationFee(BigDecimal.ZERO);
|
||||
result.setFreightAmount(BigDecimal.ZERO);
|
||||
result.setSurchargeAmount(BigDecimal.ZERO);
|
||||
result.setProductAmount(BigDecimal.ZERO);
|
||||
result.setRedlineAmount(BigDecimal.ZERO);
|
||||
result.setCostAmount(BigDecimal.ZERO);
|
||||
result.setIncomeTotal(BigDecimal.ZERO);
|
||||
result.setPayTotal(BigDecimal.ZERO);
|
||||
result.setPayableTotal(BigDecimal.ZERO);
|
||||
result.setFeeDetails(new ArrayList<TmsPriceQuoteFeeLine>());
|
||||
result.setPayFeeDetails(new ArrayList<TmsPriceQuoteFeeLine>());
|
||||
}
|
||||
else
|
||||
{
|
||||
bill.setFreightRate(result.getFreightRate());
|
||||
bill.setBaseFreightRate(result.getBaseFreightRate());
|
||||
bill.setDiscountPercent(result.getDiscountPercent());
|
||||
bill.setRegistrationFee(result.getRegistrationFee());
|
||||
bill.setFreightAmount(result.getFreightAmount());
|
||||
bill.setSurchargeAmount(result.getSurchargeAmount());
|
||||
bill.setIncomeTotal(result.getPayableTotal());
|
||||
// Payable total is decoupled from product catalog; leave null when quote has no pay_total.
|
||||
bill.setPayTotal(result.getPayTotal());
|
||||
}
|
||||
bill.setCurrency(result.getCurrency());
|
||||
bill.setRoundMode(result.getRoundMode());
|
||||
// Credit soft-fail before persist so insert/update stores arrears flag in one write.
|
||||
@@ -1155,6 +1192,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
result.setCustomerActiveStatus(customerActiveStatus);
|
||||
result.setCreditArrearsStatus(creditArrearsStatus);
|
||||
result.setRefundStatus(bill.getRefundStatus());
|
||||
result.setBillingFailStatus(billingFailStatus);
|
||||
result.setIncomeTotal(bill.getIncomeTotal());
|
||||
result.setPayTotal(bill.getPayTotal());
|
||||
result.setPayableTotal(bill.getIncomeTotal());
|
||||
@@ -1169,23 +1207,34 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
else
|
||||
{
|
||||
orderBillMapper.updatePrcOrderBill(bill);
|
||||
// Keep manually added fees (manual_flag=1) across rebill
|
||||
incomeBillFeeMapper.deleteAutoFeesByBillId(bill.getBillId());
|
||||
if (billingFailed)
|
||||
{
|
||||
// Soft-fail rebill: clear all fee details (no fee rows for failed billing).
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep manually added fees (manual_flag=1) across rebill
|
||||
incomeBillFeeMapper.deleteAutoFeesByBillId(bill.getBillId());
|
||||
}
|
||||
// Payable not in scope yet: skip prc_pay_bill_fee cleanup/write.
|
||||
// payBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
PrcIncomeBill incomeBill = persistIncomeBill(bill, result);
|
||||
List<PrcIncomeBillFee> fees = toFeeRows(bill.getBillId(),
|
||||
incomeBill == null ? null : incomeBill.getIncomeBillId(), result);
|
||||
if (existed != null)
|
||||
if (!billingFailed)
|
||||
{
|
||||
fees = filterAutoFeesAgainstManual(bill.getBillId(), fees);
|
||||
List<PrcIncomeBillFee> fees = toFeeRows(bill.getBillId(),
|
||||
incomeBill == null ? null : incomeBill.getIncomeBillId(), result);
|
||||
if (existed != null)
|
||||
{
|
||||
fees = filterAutoFeesAgainstManual(bill.getBillId(), fees);
|
||||
}
|
||||
if (!fees.isEmpty())
|
||||
{
|
||||
incomeBillFeeMapper.insertBatch(fees);
|
||||
}
|
||||
}
|
||||
if (!fees.isEmpty())
|
||||
{
|
||||
incomeBillFeeMapper.insertBatch(fees);
|
||||
}
|
||||
if (existed != null)
|
||||
if (existed != null && !billingFailed)
|
||||
{
|
||||
// Quote totals were written above; recalc so retained manual fees join income/credit
|
||||
TmsPriceQuoteResult refreshed = refreshBillIncomeTotals(bill);
|
||||
@@ -1199,7 +1248,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
result.setFeeDetails(refreshed.getFeeDetails());
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (existed == null)
|
||||
{
|
||||
bill.setRefundStatus(BillRefundStatus.NONE.getCode());
|
||||
result.setRefundStatus(bill.getRefundStatus());
|
||||
@@ -1619,6 +1668,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
r.setCustomerActiveStatus(bill.getCustomerActiveStatus());
|
||||
r.setCreditArrearsStatus(bill.getCreditArrearsStatus());
|
||||
r.setRefundStatus(bill.getRefundStatus());
|
||||
r.setBillingFailStatus(bill.getBillingFailStatus() == null
|
||||
? Integer.valueOf(BillBillingFailStatus.SUCCESS.getCode()) : bill.getBillingFailStatus());
|
||||
r.setFeeDetails(toIncomeFeeLines(incomeFees));
|
||||
r.setPayFeeDetails(toPayFeeLines(payFees));
|
||||
PrcIncomeBill incomeBill = incomeBillMapper.selectByBillId(bill.getBillId());
|
||||
|
||||
+136
-2
@@ -42,6 +42,7 @@ import com.bms.price.domain.vo.TmsPriceBillRequest;
|
||||
import com.bms.price.domain.vo.TmsPriceQuoteFeeLine;
|
||||
import com.bms.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.bms.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.bms.price.enums.BillBillingFailStatus;
|
||||
import com.bms.price.enums.BillCustomerActiveStatus;
|
||||
import com.bms.price.mapper.BmsProductCountryMapper;
|
||||
import com.bms.price.mapper.BmsProductMapper;
|
||||
@@ -64,7 +65,8 @@ import com.bms.system.service.IBmsProductCodeMappingService;
|
||||
* Shared quote/bill calc: customer bind + package version by bizDate (version effective_time).
|
||||
* Bind pr_begindate/pr_enddate is not used for billing time gate.
|
||||
* Sales price comes from package snapshot only (FIXED detail / DISCOUNT on catalog base).
|
||||
* No bind / no active version / package miss: quote and bill both fail hard (no PRODUCT_DEFAULT).
|
||||
* No bind / no active version: quote and bill both fail hard (no PRODUCT_DEFAULT).
|
||||
* Bill sync soft-fails for product/country/weight miss (billing_fail_status 1/2/3, zero amounts).
|
||||
* product_country is only for product_amount/cost and DISCOUNT freight base.
|
||||
*
|
||||
* @author bms
|
||||
@@ -207,7 +209,11 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
String customerActiveStatus = softResolveCustomer(request);
|
||||
Date bizDate = resolveBizDate(request.getBizDate());
|
||||
TmsPriceQuoteResult result = doQuote(request, bizDate);
|
||||
applySurchargesAndFees(result);
|
||||
if (!BillBillingFailStatus.isFail(result.getBillingFailStatus()))
|
||||
{
|
||||
applySurchargesAndFees(result);
|
||||
result.setBillingFailStatus(BillBillingFailStatus.SUCCESS.getCode());
|
||||
}
|
||||
result.setCustomerActiveStatus(customerActiveStatus);
|
||||
return result;
|
||||
}
|
||||
@@ -405,6 +411,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
boolean productExcludedBySetProducts = false;
|
||||
boolean attemptedPriceMatch = false;
|
||||
boolean anyZoneRequired = false;
|
||||
// Highest specificity among soft-failable misses: WEIGHT(3) > COUNTRY(2) > PRODUCT(1).
|
||||
int missCode = 0;
|
||||
// Match version by exact bill datetime (effective_time / expire_time), not end-of-day.
|
||||
Date asOf = bizDate != null ? bizDate : new Date();
|
||||
for (PrcCustomerPriceSet bind : binds)
|
||||
@@ -450,6 +458,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
if (anyProducts != null && !anyProducts.isEmpty())
|
||||
{
|
||||
productExcludedBySetProducts = true;
|
||||
missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -482,11 +491,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
stampRequestMeta(request, hit);
|
||||
return hit;
|
||||
}
|
||||
missCode = Math.max(missCode, diagnoseFixedMiss(version.getPsvId(), productCode, cc, cn, zone.zoneCode));
|
||||
}
|
||||
else if (TYPE_DISCOUNT.equals(setType))
|
||||
{
|
||||
if (catalog == null)
|
||||
{
|
||||
missCode = Math.max(missCode, diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate));
|
||||
continue;
|
||||
}
|
||||
TmsPriceQuoteResult hit = tryDiscount(bind, set, version, catalog, productCode, productName, weight, bizDate);
|
||||
@@ -495,6 +506,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
stampRequestMeta(request, hit);
|
||||
return hit;
|
||||
}
|
||||
// Discount detail missing for product (or invalid discount) after catalog hit.
|
||||
missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,6 +519,15 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
throw new ServiceException(MessageUtils.message(msgKey));
|
||||
}
|
||||
if (!attemptedPriceMatch && productExcludedBySetProducts)
|
||||
{
|
||||
missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode());
|
||||
}
|
||||
if (request instanceof TmsPriceBillRequest && BillBillingFailStatus.isFail(Integer.valueOf(missCode)))
|
||||
{
|
||||
return buildBillingMissResult(request, productCode, productName, countryCode, countryName,
|
||||
weight, bizDate, missCode);
|
||||
}
|
||||
if (!attemptedPriceMatch && productExcludedBySetProducts)
|
||||
{
|
||||
String msgKey = request instanceof TmsPriceBillRequest
|
||||
? "price.bill.productNotInPackage"
|
||||
@@ -528,6 +550,118 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
throw new ServiceException(MessageUtils.message(missKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnose FIXED miss: product rows absent -> 1; country/zone absent -> 2; else weight -> 3.
|
||||
*/
|
||||
private int diagnoseFixedMiss(Long psvId, String productCode, String countryCode,
|
||||
String countryName, String zoneCode)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return BillBillingFailStatus.PRODUCT.getCode();
|
||||
}
|
||||
int byProduct = detailMapper.countByPsvProduct(psvId, productCode);
|
||||
if (byProduct <= 0)
|
||||
{
|
||||
return BillBillingFailStatus.PRODUCT.getCode();
|
||||
}
|
||||
int byCountry = detailMapper.countByPsvProductCountryZone(
|
||||
psvId, productCode, countryCode, countryName, zoneCode);
|
||||
if (byCountry <= 0)
|
||||
{
|
||||
return BillBillingFailStatus.COUNTRY.getCode();
|
||||
}
|
||||
return BillBillingFailStatus.WEIGHT.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnose catalog miss for DISCOUNT base price.
|
||||
*/
|
||||
private int diagnoseCatalogMiss(String productCode, String countryCode, String countryName,
|
||||
String zoneCode, Date bizDate)
|
||||
{
|
||||
if (StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return BillBillingFailStatus.PRODUCT.getCode();
|
||||
}
|
||||
int byProduct = productCountryMapper.countByProduct(productCode, bizDate);
|
||||
if (byProduct <= 0)
|
||||
{
|
||||
return BillBillingFailStatus.PRODUCT.getCode();
|
||||
}
|
||||
int byCountry = productCountryMapper.countByProductCountryZone(
|
||||
productCode, countryCode, countryName, zoneCode, bizDate);
|
||||
if (byCountry <= 0)
|
||||
{
|
||||
return BillBillingFailStatus.COUNTRY.getCode();
|
||||
}
|
||||
return BillBillingFailStatus.WEIGHT.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft-fail bill result: keep order snapshot fields, all amounts zero, set billingFailStatus.
|
||||
*/
|
||||
private TmsPriceQuoteResult buildBillingMissResult(TmsPriceQuoteRequest request, String productCode,
|
||||
String productName, String countryCode, String countryName, BigDecimal weight, Date bizDate,
|
||||
int missCode)
|
||||
{
|
||||
TmsPriceQuoteResult r = new TmsPriceQuoteResult();
|
||||
r.setBillingFailStatus(Integer.valueOf(missCode));
|
||||
r.setMatchSource("BILLING_FAIL");
|
||||
r.setMatchMessage(billingFailMessage(missCode));
|
||||
r.setProductCode(productCode);
|
||||
r.setProductName(productName);
|
||||
r.setCountryCode(countryCode);
|
||||
r.setCountryName(countryName);
|
||||
r.setWeight(weight);
|
||||
r.setChargeWeight(weight);
|
||||
r.setBizDate(bizDate);
|
||||
r.setFreightRate(BigDecimal.ZERO);
|
||||
r.setBaseFreightRate(BigDecimal.ZERO);
|
||||
r.setDiscountPercent(null);
|
||||
r.setRegistrationFee(BigDecimal.ZERO);
|
||||
r.setFreightAmount(BigDecimal.ZERO);
|
||||
r.setSurchargeAmount(BigDecimal.ZERO);
|
||||
r.setProductAmount(BigDecimal.ZERO);
|
||||
r.setRedlineAmount(BigDecimal.ZERO);
|
||||
r.setCostAmount(BigDecimal.ZERO);
|
||||
r.setIncomeTotal(BigDecimal.ZERO);
|
||||
r.setPayTotal(BigDecimal.ZERO);
|
||||
r.setPayableTotal(BigDecimal.ZERO);
|
||||
r.setCurrency("CNY");
|
||||
r.setRoundMode("HALF_UP");
|
||||
r.setFromCache(Boolean.FALSE);
|
||||
r.setFeeDetails(Collections.<TmsPriceQuoteFeeLine>emptyList());
|
||||
r.setPayFeeDetails(Collections.<TmsPriceQuoteFeeLine>emptyList());
|
||||
stampRequestMeta(request, r);
|
||||
if (request instanceof TmsPriceBillRequest)
|
||||
{
|
||||
TmsPriceBillRequest billReq = (TmsPriceBillRequest) request;
|
||||
if (StringUtils.isEmpty(r.getCustomerName()) && StringUtils.isNotEmpty(billReq.getCustomerName()))
|
||||
{
|
||||
r.setCustomerName(billReq.getCustomerName());
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private String billingFailMessage(int missCode)
|
||||
{
|
||||
if (missCode == BillBillingFailStatus.PRODUCT.getCode())
|
||||
{
|
||||
return MessageUtils.message("price.bill.billingFail.product");
|
||||
}
|
||||
if (missCode == BillBillingFailStatus.COUNTRY.getCode())
|
||||
{
|
||||
return MessageUtils.message("price.bill.billingFail.country");
|
||||
}
|
||||
if (missCode == BillBillingFailStatus.WEIGHT.getCode())
|
||||
{
|
||||
return MessageUtils.message("price.bill.billingFail.weight");
|
||||
}
|
||||
return MessageUtils.message("price.bill.packagePriceNotHit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft lookup of package product row; null when version has no such product.
|
||||
*/
|
||||
|
||||
@@ -317,6 +317,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="countByProduct" resultType="int">
|
||||
select count(1)
|
||||
from product_country
|
||||
where product_code = #{productCode}
|
||||
and ifnull(is_del, '0') = '0'
|
||||
<if test="bizDate != null">
|
||||
and (effective_date is null or effective_date <= #{bizDate})
|
||||
and (expire_date is null or expire_date > #{bizDate})
|
||||
</if>
|
||||
<if test="bizDate == null">
|
||||
and expire_date is null
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="countByProductCountryZone" resultType="int">
|
||||
select count(1)
|
||||
from product_country
|
||||
where product_code = #{productCode}
|
||||
and ifnull(is_del, '0') = '0'
|
||||
<choose>
|
||||
<when test="countryCode != null and countryCode != ''">
|
||||
and upper(country_code) = upper(#{countryCode})
|
||||
</when>
|
||||
<when test="countryName != null and countryName != ''">
|
||||
and country_name = #{countryName}
|
||||
</when>
|
||||
<otherwise>
|
||||
and 1 = 0
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="zoneCode != null and zoneCode != ''">
|
||||
and upper(ifnull(zone_code,'')) = upper(#{zoneCode})
|
||||
</when>
|
||||
<otherwise>
|
||||
and (zone_code is null or zone_code = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="bizDate != null">
|
||||
and (effective_date is null or effective_date <= #{bizDate})
|
||||
and (expire_date is null or expire_date > #{bizDate})
|
||||
</if>
|
||||
<if test="bizDate == null">
|
||||
and expire_date is null
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectOpenByProductCode" resultMap="BmsProductCountryResult">
|
||||
<include refid="selectVo"/>
|
||||
where c.product_code = #{productCode}
|
||||
|
||||
@@ -71,6 +71,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="customerActiveStatus" column="customer_active_status" />
|
||||
<result property="creditArrearsStatus" column="credit_arrears_status" />
|
||||
<result property="refundStatus" column="refund_status" />
|
||||
<result property="billingFailStatus" column="billing_fail_status" />
|
||||
<result property="roundMode" column="round_mode" />
|
||||
<result property="createBy" column="created_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@@ -94,7 +95,7 @@ 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, round_mode,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, round_mode,
|
||||
created_by, create_time, updated_by, update_time
|
||||
from prc_order_bill p
|
||||
</sql>
|
||||
@@ -530,7 +531,7 @@ 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, round_mode,
|
||||
customer_active_status, credit_arrears_status, refund_status, billing_fail_status, round_mode,
|
||||
create_time
|
||||
) values (
|
||||
#{billId}, #{billKey}, #{orderNo}, #{trackingNo}, #{transferNo},
|
||||
@@ -544,7 +545,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
#{weightMin}, #{weightMax}, #{weightLabel}, #{effectiveDate}, #{chargeWeight},
|
||||
#{freightRate}, #{baseFreightRate}, #{discountPercent}, #{registrationFee},
|
||||
#{freightAmount}, #{surchargeAmount}, #{incomeTotal}, #{payTotal}, #{currency}, #{arBillStatus},
|
||||
#{customerActiveStatus}, #{creditArrearsStatus}, #{refundStatus}, #{roundMode},
|
||||
#{customerActiveStatus}, #{creditArrearsStatus}, #{refundStatus}, #{billingFailStatus}, #{roundMode},
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
@@ -614,6 +615,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
customer_active_status = #{customerActiveStatus},
|
||||
credit_arrears_status = #{creditArrearsStatus},
|
||||
refund_status = #{refundStatus},
|
||||
billing_fail_status = #{billingFailStatus},
|
||||
round_mode = #{roundMode},
|
||||
update_time = sysdate()
|
||||
where bill_id = #{billId}
|
||||
|
||||
@@ -462,6 +462,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<select id="countByPsvProduct" resultType="int">
|
||||
select count(1)
|
||||
from prc_price_set_detail
|
||||
where psv_id = #{psvId}
|
||||
and product_code = #{productCode}
|
||||
</select>
|
||||
|
||||
<select id="countByPsvProductCountryZone" resultType="int">
|
||||
select count(1)
|
||||
from prc_price_set_detail
|
||||
where psv_id = #{psvId}
|
||||
and product_code = #{productCode}
|
||||
<choose>
|
||||
<when test="countryCode != null and countryCode != ''">
|
||||
and upper(ifnull(country_code,'')) = upper(#{countryCode})
|
||||
</when>
|
||||
<when test="countryName != null and countryName != ''">
|
||||
and country_name = #{countryName}
|
||||
</when>
|
||||
<otherwise>
|
||||
and 1 = 0
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="zoneCode != null and zoneCode != ''">
|
||||
and upper(ifnull(zone_code,'')) = upper(#{zoneCode})
|
||||
</when>
|
||||
<otherwise>
|
||||
and (zone_code is null or zone_code = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<select id="selectDiscountMatch" resultMap="PrcPriceSetDetailResult">
|
||||
<include refid="selectPrcPriceSetDetailVo"/>
|
||||
where price_set_id = #{priceSetId}
|
||||
|
||||
@@ -1052,6 +1052,7 @@ 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未匹配重量段',
|
||||
`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,
|
||||
@@ -1068,6 +1069,7 @@ CREATE TABLE `prc_order_bill` (
|
||||
KEY `idx_order_bill_customer_active` (`customer_active_status`),
|
||||
KEY `idx_order_bill_credit_arrears` (`credit_arrears_status`),
|
||||
KEY `idx_order_bill_refund_status` (`refund_status`),
|
||||
KEY `idx_order_bill_billing_fail_status` (`billing_fail_status`),
|
||||
KEY `idx_order_bill_psv` (`psv_id`),
|
||||
KEY `idx_order_bill_psd` (`psd_id`),
|
||||
KEY `idx_ar_order_bill_status_scope` (`customer_id`,`currency`,`ar_bill_status`,`biz_date`,`bill_id`)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- prc_order_bill.billing_fail_status: 0 success / 1 product / 2 country / 3 weight
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @col := (
|
||||
SELECT COLUMN_NAME FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'prc_order_bill'
|
||||
AND COLUMN_NAME = 'billing_fail_status'
|
||||
LIMIT 1
|
||||
);
|
||||
SET @sql := IF(
|
||||
@col IS NULL,
|
||||
'ALTER TABLE prc_order_bill ADD COLUMN billing_fail_status tinyint NOT NULL DEFAULT 0 COMMENT ''计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段'' AFTER refund_status, ADD KEY idx_order_bill_billing_fail_status (billing_fail_status)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
@@ -58,6 +58,9 @@
|
||||
<template #col-matchSource="{ row }">
|
||||
<el-tag size="small" :type="sourceTagType(row.matchSource)">{{ sourceLabel(row.matchSource) }}</el-tag>
|
||||
</template>
|
||||
<template #col-billingFailStatus="{ row }">
|
||||
<el-tag size="small" :type="billingFailTagType(row.billingFailStatus)">{{ billingFailLabel(row.billingFailStatus) }}</el-tag>
|
||||
</template>
|
||||
<template #col-customerActiveStatus="{ row }">
|
||||
<el-tag size="small" :type="customerActiveTagType(row.customerActiveStatus)">{{ customerActiveLabel(row.customerActiveStatus) }}</el-tag>
|
||||
</template>
|
||||
@@ -243,6 +246,7 @@ const columns = [
|
||||
{ label: '应收总额', prop: 'incomeTotal', align: 'right', width: 120, slot: 'incomeTotal' },
|
||||
{ label: '应付总额', prop: 'payTotal', align: 'right', width: 120, slot: 'payTotal' },
|
||||
{ label: '计价来源', prop: 'matchSource', align: 'center', width: 130, slot: 'matchSource' },
|
||||
{ label: '计费状态', prop: 'billingFailStatus', align: 'center', width: 120, slot: 'billingFailStatus' },
|
||||
{ label: '客户状态', prop: 'customerActiveStatus', align: 'center', width: 100, slot: 'customerActiveStatus' },
|
||||
{ label: '额度欠费', prop: 'creditArrearsStatus', align: 'center', width: 100, slot: 'creditArrearsStatus' },
|
||||
// { label: '退费状态', prop: 'refundStatus', align: 'center', width: 100, slot: 'refundStatus' },
|
||||
@@ -523,11 +527,26 @@ function formatWeight(val) {
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function sourceLabel(source) {
|
||||
const map = {
|
||||
PACKAGE_FIXED: '客户固定价套餐',
|
||||
PACKAGE_DISCOUNT: '客户折扣套餐',
|
||||
PRODUCT_DEFAULT: '产品默认价',
|
||||
BILLING_FAIL: '计费失败',
|
||||
FIXED: '客户固定价套餐',
|
||||
DISCOUNT: '客户折扣套餐'
|
||||
}
|
||||
@@ -538,6 +557,7 @@ function sourceTagType(source) {
|
||||
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
||||
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
||||
if (source === 'PRODUCT_DEFAULT') return 'info'
|
||||
if (source === 'BILLING_FAIL') return 'danger'
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@
|
||||
<el-descriptions-item label="计价来源">
|
||||
<el-tag size="small" :type="sourceTagType(detail.matchSource)">{{ sourceLabel(detail.matchSource) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计费状态">
|
||||
<el-tag size="small" :type="billingFailTagType(detail.billingFailStatus)">{{ billingFailLabel(detail.billingFailStatus) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐">{{ priceSetLabel(detail) }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="套餐版本">{{ detail.versionNo || (detail.psvId ? ('psv=' + detail.psvId) : '-') }}</el-descriptions-item> -->
|
||||
<!-- <el-descriptions-item label="套餐明细ID">{{ detail.psdId != null ? detail.psdId : '-' }}</el-descriptions-item> -->
|
||||
@@ -332,11 +335,26 @@ function formatDateTime(val) {
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
|
||||
}
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function sourceLabel(source) {
|
||||
const map = {
|
||||
PACKAGE_FIXED: '客户固定价套餐',
|
||||
PACKAGE_DISCOUNT: '客户折扣套餐',
|
||||
PRODUCT_DEFAULT: '产品默认价',
|
||||
BILLING_FAIL: '计费失败',
|
||||
FIXED: '客户固定价套餐',
|
||||
DISCOUNT: '客户折扣套餐'
|
||||
}
|
||||
@@ -379,6 +397,7 @@ function sourceTagType(source) {
|
||||
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
||||
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
||||
if (source === 'PRODUCT_DEFAULT') return 'info'
|
||||
if (source === 'BILLING_FAIL') return 'danger'
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user