✨ 应收账单保存计费失败状态并导出说明及黄色标注

This commit is contained in:
caojia
2026-10-14 17:50:03 +08:00
parent 996d854492
commit e4438d6f01
8 changed files with 157 additions and 51 deletions
@@ -28,6 +28,8 @@ public class ArBillDetail implements Serializable {
private String productCode;
private String productName;
private String goodsDescription;
/** 生成账单时的计费失败状态快照:0成功/1未匹配产品/2未匹配国家/3未匹配重量段。 */
private Integer billingFailStatus;
private String countryCode;
private String countryName;
private String destPostcode;
@@ -28,6 +28,10 @@ import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.io.ClassPathResource;
@@ -85,6 +89,7 @@ import com.bms.common.utils.uuid.IdUtils;
import com.bms.price.domain.PrcIncomeBill;
import com.bms.price.domain.PrcIncomeBillFee;
import com.bms.price.domain.PrcOrderBill;
import com.bms.price.enums.BillBillingFailStatus;
import com.bms.price.mapper.PrcIncomeBillMapper;
import com.bms.price.mapper.PrcIncomeBillFeeMapper;
import com.bms.price.mapper.PrcOrderBillMapper;
@@ -105,7 +110,7 @@ public class ArBillServiceImpl implements IArBillService {
*/
private static final int TEMPLATE_DATA_START_ROW = 5;
private static final int TEMPLATE_FOOTER_START_ROW = 5;
private static final int TEMPLATE_COLUMN_COUNT = 20;
private static final int TEMPLATE_COLUMN_COUNT = 21;
/** 客户订单号、运单号位于 Excel 的 C、D 列,按给定样例长度并额外保留两字符边距。 */
private static final int CUSTOMER_ORDER_NO_COLUMN_INDEX = 3;
private static final int TRACKING_NO_COLUMN_INDEX = 4;
@@ -135,6 +140,7 @@ public class ArBillServiceImpl implements IArBillService {
*/
private static final int TEMPLATE_DETAIL_STYLE_INDEX = 31;
private static final String TEMPLATE_SHEET_XML = "xl/worksheets/sheet1.xml";
private static final String TEMPLATE_STYLES_XML = "xl/styles.xml";
private static final Pattern TEMPLATE_COLUMN_PATTERN = Pattern.compile("<col\\b([^>]*)/\\s*>", Pattern.DOTALL);
private static final Pattern TEMPLATE_COLUMN_MIN_PATTERN = Pattern.compile("\\bmin\\s*=\\s*[\"'](\\d+)[\"']");
private static final Pattern TEMPLATE_COLUMN_MAX_PATTERN = Pattern.compile("\\bmax\\s*=\\s*[\"'](\\d+)[\"']");
@@ -682,8 +688,8 @@ public class ArBillServiceImpl implements IArBillService {
// 先抓取本批次的未出账费用快照,后续只更新和复制这些费用,避免把同订单已有 BILLED 费用重复带入新账单。
List<PrcIncomeBillFee> sourceFees = sourceFeeMapper.selectUnbilledByBillIds(
currentSourceBillIds, ArIncomeBillFeeStatus.UNBILLED.getCode());
if (sourceFees == null || sourceFees.isEmpty()) {
throw new ServiceException("当前批次没有可生成的未出账费用");
if (sourceFees == null) {
sourceFees = new ArrayList<>();
}
Map<String, Integer> sourceFeeCounts = new HashMap<>();
List<String> sourceFeeIds = new ArrayList<>(sourceFees.size());
@@ -691,9 +697,12 @@ public class ArBillServiceImpl implements IArBillService {
sourceFeeCounts.merge(sourceFee.getBillId(), 1, Integer::sum);
sourceFeeIds.add(sourceFee.getFeeId());
}
for (String sourceBillId : currentSourceBillIds) {
if (!sourceFeeCounts.containsKey(sourceBillId)) {
throw new ServiceException("来源订单没有可生成的未出账费用:" + sourceBillId);
for (PrcOrderBill source : sources) {
BillBillingFailStatus billingFailStatus = BillBillingFailStatus.of(source.getBillingFailStatus());
if (!sourceFeeCounts.containsKey(source.getBillId())
&& (billingFailStatus == null || billingFailStatus == BillBillingFailStatus.SUCCESS
|| nvl(source.getIncomeTotal()).compareTo(BigDecimal.ZERO) != 0)) {
throw new ServiceException("来源订单没有可生成的未出账费用:" + source.getBillId());
}
}
@@ -702,12 +711,14 @@ public class ArBillServiceImpl implements IArBillService {
if (claimedCount != currentSourceBillIds.size()) {
throw new ServiceException("部分计费订单已被其他账单占用,请重新查询后生成");
}
int billedFeeCount = sourceFeeMapper.updateArBillStatusByFeeIds(sourceFeeIds,
ArIncomeBillFeeStatus.UNBILLED.getCode(), ArIncomeBillFeeStatus.BILLED.getCode());
if (billedFeeCount != sourceFeeIds.size()) {
throw new ServiceException("部分费用状态已发生变化,请重新查询后生成");
// 纯失败订单批次没有费用,不能执行空 IN 更新。
if (!sourceFeeIds.isEmpty()) {
int billedFeeCount = sourceFeeMapper.updateArBillStatusByFeeIds(sourceFeeIds,
ArIncomeBillFeeStatus.UNBILLED.getCode(), ArIncomeBillFeeStatus.BILLED.getCode());
if (billedFeeCount != sourceFeeIds.size()) {
throw new ServiceException("部分费用状态已发生变化,请重新查询后生成");
}
}
sourceBillMapper.refreshArBillStatusByBillIds(currentSourceBillIds);
int detailNo = (task.getProcessedCount() == null ? 0 : task.getProcessedCount()) + 1;
List<ArBillDetail> details = new ArrayList<>(sources.size());
@@ -716,6 +727,8 @@ public class ArBillServiceImpl implements IArBillService {
detailNo++, task.getCreateBy()));
}
detailMapper.insertBatch(details);
// 失败订单先落快照,再汇总占用状态,避免无费用订单被错误恢复为 UNBILLED。
sourceBillMapper.refreshArBillStatusByBillIds(currentSourceBillIds);
Map<String, ArBillDetail> detailsBySourceBill = new HashMap<>();
for (ArBillDetail detail : detailMapper.selectByBillIdAndSourceIds(bill.getArBillId(), currentSourceBillIds)) {
@@ -1165,6 +1178,7 @@ public class ArBillServiceImpl implements IArBillService {
detail.setProductCode(source.getProductCode());
detail.setProductName(source.getProductName());
detail.setGoodsDescription(source.getOrderRemark());
detail.setBillingFailStatus(source.getBillingFailStatus());
detail.setCountryCode(source.getCountryCode());
detail.setCountryName(source.getCountryName());
detail.setDestPostcode(source.getDestPostcode());
@@ -1262,9 +1276,16 @@ public class ArBillServiceImpl implements IArBillService {
}
/**
* 复制模板 ZIP 包,仅替换 sheet1.xml,保持模板样式、合并单元格和其他资源不变。
* 复制模板 ZIP 包,替换明细工作表并追加黄色明细样式,保留模板其他资源。
*/
private void writeOoxmlWorkbook(ArBill bill, ArExportTemplate template, Path target) throws IOException {
// 样式表单独读取;不加载整张工作簿,且不依赖 styles.xml 和 sheet1.xml 在 ZIP 中的先后顺序。
StylesTable styles = readExportStyles(template);
XSSFCellStyle failedDetailStyle = styles.createCellStyle();
failedDetailStyle.cloneStyleFrom(styles.getStyleAt(TEMPLATE_DETAIL_STYLE_INDEX));
failedDetailStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
failedDetailStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
int failedDetailStyleIndex = Short.toUnsignedInt(failedDetailStyle.getIndex());
try (InputStream input = openTemplateStream(template); ZipInputStream templateZip = new ZipInputStream(input);
OutputStream fileOutput = Files.newOutputStream(target); ZipOutputStream output = new ZipOutputStream(fileOutput)) {
boolean replaced = false;
@@ -1272,8 +1293,10 @@ public class ArBillServiceImpl implements IArBillService {
ZipEntry outputEntry = new ZipEntry(entry.getName());
output.putNextEntry(outputEntry);
if (TEMPLATE_SHEET_XML.equals(entry.getName())) {
writeOoxmlSheet(readUtf8(templateZip), bill, output);
writeOoxmlSheet(readUtf8(templateZip), bill, output, failedDetailStyleIndex);
replaced = true;
} else if (TEMPLATE_STYLES_XML.equals(entry.getName())) {
styles.writeTo(output);
} else if (!entry.isDirectory()) {
templateZip.transferTo(output);
}
@@ -1286,6 +1309,22 @@ public class ArBillServiceImpl implements IArBillService {
}
}
private StylesTable readExportStyles(ArExportTemplate template) throws IOException {
try (InputStream input = openTemplateStream(template); ZipInputStream templateZip = new ZipInputStream(input)) {
for (ZipEntry entry; (entry = templateZip.getNextEntry()) != null; ) {
if (TEMPLATE_STYLES_XML.equals(entry.getName())) {
StylesTable styles = new StylesTable(templateZip);
if (styles.getNumCellStyles() <= TEMPLATE_DETAIL_STYLE_INDEX) {
throw new IOException("应收账单导出模板缺少明细样式: " + TEMPLATE_DETAIL_STYLE_INDEX);
}
return styles;
}
templateZip.closeEntry();
}
}
throw new IOException("应收账单导出模板缺少样式表: " + TEMPLATE_STYLES_XML);
}
private InputStream openTemplateStream(ArExportTemplate template) throws IOException {
String storageType = StringUtils.isEmpty(template.getStorageType()) ? "CLASSPATH" : template.getStorageType();
String storageKey = StringUtils.isEmpty(template.getStorageKey()) ? EXPORT_TEMPLATE_RESOURCE : template.getStorageKey();
@@ -1313,7 +1352,8 @@ public class ArBillServiceImpl implements IArBillService {
/**
* 以 XML 流方式写入表头、分页明细和银行信息,避免 POI 在大数据量下移动行。
*/
private void writeOoxmlSheet(String templateXml, ArBill bill, OutputStream output) throws IOException {
private void writeOoxmlSheet(String templateXml, ArBill bill, OutputStream output, int failedDetailStyleIndex)
throws IOException {
int sheetDataStart = templateXml.indexOf("<sheetData");
int sheetDataOpenEnd = templateXml.indexOf('>', sheetDataStart);
int sheetDataEnd = templateXml.indexOf("</sheetData>", sheetDataOpenEnd);
@@ -1332,7 +1372,7 @@ public class ArBillServiceImpl implements IArBillService {
writeUtf8(output, headerRow);
writeUtf8(output, templateRows.get(2));
String customerRow = replaceCellXml(templateRows.get(3), "C3", bill.getCustomerName(), false);
// 账单所属期区域为 O3:T3 合并单元格,必须写入左上角 O3 才能显示。
// 账单所属期区域为 O3:U3 合并单元格,必须写入左上角 O3 才能显示。
customerRow = replaceCellXml(customerRow, "O3", exportReceiveTimeRange(receiveTimeRange), false);
writeUtf8(output, customerRow);
writeUtf8(output, templateRows.get(4));
@@ -1346,7 +1386,7 @@ public class ArBillServiceImpl implements IArBillService {
}
attachFees(details);
for (ArBillDetail detail : details) {
writeOoxmlDetailRow(output, rowIndex++, detail);
writeOoxmlDetailRow(output, rowIndex++, detail, failedDetailStyleIndex);
}
lastDetailNo = details.get(details.size() - 1).getDetailNo();
}
@@ -1490,7 +1530,10 @@ public class ArBillServiceImpl implements IArBillService {
}
}
private void writeOoxmlDetailRow(OutputStream output, int rowNumber, ArBillDetail detail) throws IOException {
private void writeOoxmlDetailRow(OutputStream output, int rowNumber, ArBillDetail detail, int failedDetailStyleIndex)
throws IOException {
String failureDescription = exportBillingFailDescription(detail.getBillingFailStatus());
int styleIndex = failureDescription.isEmpty() ? TEMPLATE_DETAIL_STYLE_INDEX : failedDetailStyleIndex;
Object[] values = {
detail.getDetailNo(),
detail.getReceiveTime() == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(detail.getReceiveTime()),
@@ -1500,19 +1543,33 @@ public class ArBillServiceImpl implements IArBillService {
detail.getDestPostcode(), decimal(detail.getReceiveWeight(), 3), decimal(detail.getPkgLength(), 2),
decimal(detail.getPkgHeight(), 2), decimal(detail.getPkgWidth(), 2), decimal(detail.getVolumeWeight(), 3),
decimal(detail.getChargeWeight(), 3), exportUnitPrice(detail), exportProcessingFee(detail),
exportFormula(detail), exportFeeType(), detail.getCurrency(), detail.getTotalAmount(), exportRemark(detail)
exportFormula(detail), exportFeeType(), detail.getCurrency(), detail.getTotalAmount(), exportRemark(detail),
failureDescription
};
StringBuilder row = new StringBuilder(2048);
row.append("<row r=\"").append(rowNumber).append("\" ht=\"24.0\" customHeight=\"true\" spans=\"1:20\">");
row.append("<row r=\"").append(rowNumber).append("\" ht=\"24.0\" customHeight=\"true\" spans=\"1:21\">");
for (int index = 0; index < TEMPLATE_COLUMN_COUNT; index++) {
appendOoxmlCell(row, (char) ('A' + index) + String.valueOf(rowNumber), values[index]);
appendOoxmlCell(row, (char) ('A' + index) + String.valueOf(rowNumber), values[index], styleIndex);
}
row.append("</row>");
writeUtf8(output, row.toString());
}
private void appendOoxmlCell(StringBuilder row, String reference, Object value) {
row.append("<c r=\"").append(reference).append("\" s=\"").append(TEMPLATE_DETAIL_STYLE_INDEX).append("\"");
private String exportBillingFailDescription(Integer billingFailStatus) {
BillBillingFailStatus status = BillBillingFailStatus.of(billingFailStatus);
if (status == null) {
return "";
}
return switch (status) {
case PRODUCT -> "未匹配产品";
case COUNTRY -> "未匹配国家";
case WEIGHT -> "未匹配重量段";
case SUCCESS -> "";
};
}
private void appendOoxmlCell(StringBuilder row, String reference, Object value, int styleIndex) {
row.append("<c r=\"").append(reference).append("\" s=\"").append(styleIndex).append("\"");
if (value instanceof Number) {
row.append(" t=\"n\"><v>").append(numberText((Number) value)).append("</v></c>");
} else {
@@ -78,10 +78,10 @@ public interface PrcOrderBillMapper
int releaseArBillOrdersByArBillId(@Param("arBillId") Long arBillId, @Param("operator") String operator,
@Param("fromStatus") String fromStatus, @Param("toStatus") String toStatus);
/** 根据费用明细状态汇总订单的 UNBILLED/PARTIAL/BILLED 状态。 */
/** 根据费用明细状态及无费用失败订单的有效账单快照汇总订单出账状态。 */
int refreshArBillStatusByBillIds(@Param("billIds") List<String> billIds);
/** 按应收账单关联的来源订单批量汇总状态,适用于大账单清理和作废。 */
/** 清理或作废时批量释放来源订单;无费用失败订单的占用判断排除当前账单。 */
int refreshArBillStatusByArBillId(@Param("arBillId") Long arBillId);
public int insertPrcOrderBill(PrcOrderBill bill);
@@ -16,6 +16,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="productCode" column="product_code"/>
<result property="productName" column="product_name"/>
<result property="goodsDescription" column="goods_description"/>
<result property="billingFailStatus" column="billing_fail_status"/>
<result property="countryCode" column="country_code"/>
<result property="countryName" column="country_name"/>
<result property="destPostcode" column="dest_postcode"/>
@@ -41,7 +42,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
actual_weight, receive_weight, volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height,
currency, total_amount, product_amount, redline_amount, remark, create_by, create_time
currency, total_amount, product_amount, redline_amount, billing_fail_status, remark, create_by, create_time
from ar_bill_detail where ar_bill_id = #{arBillId} order by detail_no
</select>
@@ -49,7 +50,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
actual_weight, receive_weight, volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height,
currency, total_amount, product_amount, redline_amount, remark, create_by, create_time
currency, total_amount, product_amount, redline_amount, billing_fail_status, remark, create_by, create_time
from ar_bill_detail
where ar_bill_id = #{arBillId}
and prc_order_bill_id in
@@ -87,7 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select d.detail_id, d.ar_bill_id, d.prc_order_bill_id, d.detail_no, d.order_no, d.tracking_no, d.transfer_no, d.receive_time,
d.product_code, d.product_name, d.goods_description, d.country_code, d.country_name, d.dest_postcode,
d.actual_weight, d.receive_weight, d.volume_weight, d.charge_weight, d.freight_rate, d.registration_fee, d.pkg_length, d.pkg_width, d.pkg_height,
d.currency, d.total_amount, d.product_amount, d.redline_amount, d.remark, d.create_by, d.create_time,
d.currency, d.total_amount, d.product_amount, d.redline_amount, d.billing_fail_status, d.remark, d.create_by, d.create_time,
f.detail_fee_id as fee_detail_id, f.detail_id as fee_parent_detail_id,
f.prc_income_bill_fee_id as fee_prc_income_bill_fee_id,
f.fee_no, f.fee_type, f.fee_name, f.sc_id as fee_sc_id, f.quantity as fee_quantity,
@@ -106,7 +107,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select detail_id, ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time,
product_code, product_name, goods_description, country_code, country_name, dest_postcode,
actual_weight, receive_weight, volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height,
currency, total_amount, product_amount, redline_amount, remark, create_by, create_time
currency, total_amount, product_amount, redline_amount, billing_fail_status, remark, create_by, create_time
from ar_bill_detail
where ar_bill_id = #{arBillId} and detail_no &gt; #{lastDetailNo}
order by detail_no
@@ -125,12 +126,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
product_name, goods_description, country_code, country_name, dest_postcode,
actual_weight, receive_weight,
volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height, currency,
total_amount, product_amount, redline_amount,
total_amount, product_amount, redline_amount, billing_fail_status,
remark, create_by, create_time)
values (#{arBillId}, #{prcOrderBillId}, #{detailNo}, #{orderNo}, #{trackingNo}, #{transferNo}, #{receiveTime},
#{productCode}, #{productName}, #{goodsDescription}, #{countryCode}, #{countryName},
#{destPostcode}, #{actualWeight}, #{receiveWeight}, #{volumeWeight}, #{chargeWeight}, #{freightRate}, #{registrationFee}, #{pkgLength}, #{pkgWidth},
#{pkgHeight}, #{currency}, #{totalAmount}, #{productAmount}, #{redlineAmount}, #{remark}, #{createBy}, sysdate())
#{pkgHeight}, #{currency}, #{totalAmount}, #{productAmount}, #{redlineAmount}, #{billingFailStatus}, #{remark}, #{createBy}, sysdate())
</insert>
<insert id="insertBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="detailId">
@@ -138,7 +139,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
ar_bill_id, prc_order_bill_id, detail_no, order_no, tracking_no, transfer_no, receive_time, product_code,
product_name, goods_description, country_code, country_name, dest_postcode, actual_weight, receive_weight,
volume_weight, charge_weight, freight_rate, registration_fee, pkg_length, pkg_width, pkg_height, currency, total_amount,
product_amount, redline_amount,
product_amount, redline_amount, billing_fail_status,
remark, create_by, create_time
) values
<foreach collection="list" item="item" separator=",">
@@ -146,7 +147,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{item.transferNo}, #{item.receiveTime}, #{item.productCode}, #{item.productName}, #{item.goodsDescription},
#{item.countryCode}, #{item.countryName}, #{item.destPostcode}, #{item.actualWeight}, #{item.receiveWeight},
#{item.volumeWeight}, #{item.chargeWeight}, #{item.freightRate}, #{item.registrationFee}, #{item.pkgLength}, #{item.pkgWidth}, #{item.pkgHeight},
#{item.currency}, #{item.totalAmount}, #{item.productAmount}, #{item.redlineAmount},
#{item.currency}, #{item.totalAmount}, #{item.productAmount}, #{item.redlineAmount}, #{item.billingFailStatus},
#{item.remark}, #{item.createBy}, sysdate())
</foreach>
</insert>
@@ -118,7 +118,7 @@ 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.round_mode,
p.customer_active_status, p.credit_arrears_status, p.refund_status, p.billing_fail_status, 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
@@ -358,6 +358,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
</sql>
<!-- 有未出账费用的订单,或尚未纳入有效账单的无费用计费失败订单。 -->
<sql id="arBillEligibleSourceCondition">
and (
exists (
select 1 from prc_income_bill_fee f
where f.bill_id = p.bill_id and f.ar_bill_status = #{feeArBillStatus}
)
or (
p.billing_fail_status in (1, 2, 3)
and not exists (
select 1 from prc_income_bill_fee f where f.bill_id = p.bill_id
)
and not exists (
select 1 from ar_bill_detail d
inner join ar_bill b on b.ar_bill_id = d.ar_bill_id
where d.prc_order_bill_id = p.bill_id
and d.billing_fail_status in (1, 2, 3)
and b.status &lt;&gt; 'VOID' and b.generation_status &lt;&gt; 'FAILED'
)
)
)
</sql>
<select id="selectPrcOrderBillByCustomerPeriod" resultMap="PrcOrderBillArSourceResult">
<include refid="selectArBillSourceVo"/>
where p.customer_id = #{customerId}
@@ -385,11 +408,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<foreach collection="arBillStatuses" item="status" open="(" separator="," close=")">
#{status}
</foreach>
and exists (
select 1 from prc_income_bill_fee f
where f.bill_id = p.bill_id
and f.ar_bill_status = #{feeArBillStatus}
)
<include refid="arBillEligibleSourceCondition"/>
<if test="currency != null and currency != ''">
and p.currency = #{currency}
</if>
@@ -448,11 +467,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<foreach collection="arBillStatuses" item="status" open="(" separator="," close=")">
#{status}
</foreach>
and exists (
select 1 from prc_income_bill_fee f
where f.bill_id = p.bill_id
and f.ar_bill_status = #{feeArBillStatus}
)
<include refid="arBillEligibleSourceCondition"/>
<if test="currency != null and currency != ''">
and p.currency = #{currency}
</if>
@@ -499,11 +514,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<foreach collection="arBillStatuses" item="status" open="(" separator="," close=")">
#{status}
</foreach>
and exists (
select 1 from prc_income_bill_fee f
where f.bill_id = p.bill_id
and f.ar_bill_status = #{feeArBillStatus}
)
<include refid="arBillEligibleSourceCondition"/>
and p.bill_id in
<foreach collection="billIds" item="billId" open="(" separator="," close=")">
#{billId}
@@ -662,7 +673,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and p.ar_bill_status = #{fromStatus}
</update>
<!-- 订单状态由来源费用状态汇总,避免补录或删除费用后状态失真。 -->
<!-- 订单状态按费用汇总;无费用的失败订单以有效账单中的失败快照判断占用。 -->
<update id="refreshArBillStatusByBillIds">
update prc_order_bill o
left join (
@@ -678,7 +689,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
group by bill_id
) f on f.bill_id = o.bill_id
set o.ar_bill_status = case
when f.bill_id is null then 'UNBILLED'
when f.bill_id is null then case when exists (
select 1 from ar_bill_detail d
inner join ar_bill b on b.ar_bill_id = d.ar_bill_id
where d.prc_order_bill_id = o.bill_id
and d.billing_fail_status in (1, 2, 3)
and b.status &lt;&gt; 'VOID' and b.generation_status &lt;&gt; 'FAILED'
) then 'BILLED' else 'UNBILLED' end
when f.unbilled_count > 0 and f.billed_count > 0 then 'PARTIAL'
when f.unbilled_count > 0 then 'UNBILLED'
when f.billed_count = f.fee_count then 'BILLED'
@@ -712,7 +729,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
group by f.bill_id
) fee_summary on fee_summary.bill_id = o.bill_id
set o.ar_bill_status = case
when fee_summary.bill_id is null then 'UNBILLED'
when fee_summary.bill_id is null then case when exists (
select 1 from ar_bill_detail d
inner join ar_bill b on b.ar_bill_id = d.ar_bill_id
where d.prc_order_bill_id = o.bill_id
and d.billing_fail_status in (1, 2, 3)
and b.status &lt;&gt; 'VOID' and b.generation_status &lt;&gt; 'FAILED'
and b.ar_bill_id &lt;&gt; #{arBillId}
) then 'BILLED' else 'UNBILLED' end
when fee_summary.unbilled_count > 0 and fee_summary.billed_count > 0 then 'PARTIAL'
when fee_summary.unbilled_count > 0 then 'UNBILLED'
when fee_summary.billed_count = fee_summary.fee_count then 'BILLED'