🔥 移除应收毛利分析冗余展示和筛选

This commit is contained in:
caojia
2026-11-09 15:58:14 +08:00
parent 8633019788
commit f2d27f3502
10 changed files with 50 additions and 46 deletions
@@ -37,13 +37,10 @@ class ArProfitMapperSqlTest
assertTrue(manager.contains("find_in_set"));
assertTrue(manager.contains("b.status in"));
}
ArProfitQuery orderFilter = new ArProfitQuery();
orderFilter.setOrderNo("ORDER-1");
String bills = configuration.getMappedStatement("com.bms.ar.mapper.ArProfitMapper.selectBills")
.getBoundSql(Map.of("query", orderFilter, "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
.getBoundSql(Map.of("query", new ArProfitQuery(), "scope", new ArProfitScope(true, false, false, 8L, 10L))).getSql();
assertTrue(bills.contains("from ar_bill b"));
assertTrue(bills.contains("exists (select 1 from ar_bill_detail matched"));
assertFalse(bills.contains("and d.order_no like"));
assertFalse(bills.contains("order_no"));
String billCommission = configuration.getMappedStatement("com.bms.ar.mapper.ArCommissionMapper.selectCommissionSummary")
.getBoundSql(Map.of("billId", 9L)).getSql();
assertTrue(billCommission.contains("r.ar_bill_id = ?"));
@@ -41,8 +41,6 @@ public class ArBill extends BaseEntity {
private Long profitSalesUserId;
private String profitSalesUserCode;
private String profitSalesUserName;
/** NEW=生成时固化;BACKFILLED=历史补录,来源为补录时可取得的数据。 */
private String profitSnapshotSource;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
/** 账单所属期开始日期,导出模板展示用。 */
@@ -9,7 +9,6 @@ public class ArProfitQuery
{
private Long customerId;
private String billNo;
private String orderNo;
private String status;
private Long salesUserId;
private Long arBillId;
@@ -14,7 +14,6 @@ public class ArProfitRow
@Excel(name = "客户代码", width = 16) private String customerCode;
@Excel(name = "客户名称", width = 24) private String customerName;
private String customerArea;
@Excel(name = "归属来源", readConverterExp = "NEW=生成时快照,BACKFILLED=历史补录", width = 14) private String snapshotSource;
@Excel(name = "销售员", width = 16) private String salesUserName;
@Excel(name = "币种", width = 10) private String currency;
@Excel(name = "账单状态", readConverterExp = "CONFIRMED=已确认,PARTIAL_WRITTEN_OFF=部分核销,WRITTEN_OFF=已核销", width = 14) private String status;
@@ -8,7 +8,6 @@ public class ArProfitSummary
{
private String currency;
private Long billCount;
private Long invalidBillCount;
private BigDecimal salesAmount;
private BigDecimal productAmount;
private BigDecimal salesGrossProfit;
@@ -449,7 +449,6 @@ public class ArBillServiceImpl implements IArBillService {
String customerName = StringUtils.trim(request.getCustomerName());
bill.setCustomerCode(customerCode == null ? "" : customerCode);
bill.setCustomerName(customerName == null ? "" : customerName);
bill.setProfitSnapshotSource("NEW");
// 固定账单生成时的客户归属;无法确定的用户保持为空,由财务核对。
BmsCustomer snapshotCustomer = customerMapper.selectBmsCustomerByCustomerId(request.getCustomerId());
if (snapshotCustomer != null) {
@@ -15,7 +15,6 @@
<result property="profitSalesUserId" column="profit_sales_user_id"/>
<result property="profitSalesUserCode" column="profit_sales_user_code"/>
<result property="profitSalesUserName" column="profit_sales_user_name"/>
<result property="profitSnapshotSource" column="profit_snapshot_source"/>
<result property="billPeriodStart" column="bill_period_start"/>
<result property="billPeriodEnd" column="bill_period_end"/>
<result property="billDate" column="bill_date"/>
@@ -57,7 +56,6 @@
profit_sales_user_id,
profit_sales_user_code,
profit_sales_user_name,
profit_snapshot_source,
bill_period_start,
bill_period_end,
bill_date,
@@ -180,14 +178,14 @@
<insert id="insertArBill" parameterType="ArBill" useGeneratedKeys="true" keyProperty="arBillId">
insert into ar_bill (bill_no, bill_name, customer_id, customer_code, customer_name,
profit_customer_area, profit_sales_user_id, profit_sales_user_code, profit_sales_user_name, profit_snapshot_source, bill_period_start,
profit_customer_area, profit_sales_user_id, profit_sales_user_code, profit_sales_user_name, bill_period_start,
bill_period_end,
bill_date, currency, order_count, total_amount, written_off_amount, unwritten_amount,
status, generation_status, bill_version, bank_account_name, bank_account_no, bank_name,
remark,
confirmed_by, confirmed_time, create_by, create_user_id, create_time, update_by, update_time)
values (#{billNo}, #{billName}, #{customerId}, #{customerCode}, #{customerName},
#{profitCustomerArea}, #{profitSalesUserId}, #{profitSalesUserCode}, #{profitSalesUserName}, #{profitSnapshotSource}, #{billPeriodStart},
#{profitCustomerArea}, #{profitSalesUserId}, #{profitSalesUserCode}, #{profitSalesUserName}, #{billPeriodStart},
#{billPeriodEnd},
#{billDate}, #{currency}, #{orderCount}, #{totalAmount}, #{writtenOffAmount}, #{unwrittenAmount},
#{status}, #{generationStatus}, #{billVersion}, #{bankAccountName}, #{bankAccountNo}, #{bankName},
@@ -5,7 +5,7 @@
<id property="arBillId" column="ar_bill_id"/>
<result property="billNo" column="bill_no"/><result property="billDate" column="bill_date"/>
<result property="customerCode" column="customer_code"/><result property="customerName" column="customer_name"/>
<result property="customerArea" column="customer_area"/><result property="snapshotSource" column="snapshot_source"/>
<result property="customerArea" column="customer_area"/>
<result property="salesUserName" column="sales_user_name"/><result property="currency" column="currency"/>
<result property="status" column="status"/><result property="salesAmount" column="sales_amount"/>
<result property="productAmount" column="product_amount"/><result property="salesGrossProfit" column="sales_gross_profit"/>
@@ -14,7 +14,6 @@
</resultMap>
<resultMap id="summary" type="com.bms.ar.profit.ArProfitSummary">
<result property="currency" column="currency"/><result property="billCount" column="bill_count"/>
<result property="invalidBillCount" column="invalid_bill_count"/>
<result property="salesAmount" column="sales_amount"/><result property="productAmount" column="product_amount"/>
<result property="salesGrossProfit" column="sales_gross_profit"/><result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
<result property="writtenOffAmount" column="written_off_amount"/><result property="unwrittenAmount" column="unwritten_amount"/>
@@ -47,17 +46,12 @@
<otherwise>and 1 = 0</otherwise>
</choose>
</sql>
<!-- 订单号仅用于筛选账单,不限制该账单内参与计算的明细。 -->
<sql id="billConditions">
where b.status in ('CONFIRMED', 'PARTIAL_WRITTEN_OFF', 'WRITTEN_OFF') and b.generation_status = 'READY'
<include refid="scopeCondition"/>
<if test="query.customerId != null">and b.customer_id = #{query.customerId}</if>
<if test="query.billNo != null and query.billNo != ''">and b.bill_no like concat('%', #{query.billNo}, '%')</if>
<if test="query.arBillId != null">and b.ar_bill_id = #{query.arBillId}</if>
<if test="query.orderNo != null and query.orderNo != ''">
and exists (select 1 from ar_bill_detail matched where matched.ar_bill_id = b.ar_bill_id
and matched.order_no like concat('%', #{query.orderNo}, '%'))
</if>
<if test="query.status != null and query.status != ''">and b.status = #{query.status}</if>
<if test="query.salesUserId != null">and b.profit_sales_user_id = #{query.salesUserId}</if>
<if test="query.beginBillDate != null">and b.bill_date &gt;= #{query.beginBillDate}</if>
@@ -69,7 +63,7 @@
<bind name="shenzhenArea" value="@com.bms.price.enums.ProfitAreaRule@SHENZHEN.getCustomerArea()"/>
<bind name="guangzhouArea" value="@com.bms.price.enums.ProfitAreaRule@GUANGZHOU.getCustomerArea()"/>
select b.ar_bill_id, b.bill_no, b.bill_date, b.customer_code, b.customer_name,
b.profit_customer_area as customer_area, b.profit_snapshot_source as snapshot_source,
b.profit_customer_area as customer_area,
b.profit_sales_user_name as sales_user_name, b.currency, b.status,
b.total_amount as sales_amount, b.written_off_amount, b.unwritten_amount,
case when a.detail_count > 0 and a.invalid_count = 0 and b.total_amount is not null
@@ -118,7 +112,7 @@
order by b.bill_date desc, b.ar_bill_id desc
</select>
<select id="selectSummary" resultMap="summary">
select t.currency, count(*) as bill_count, sum(t.profit_incomplete) as invalid_bill_count,
select t.currency, count(*) as bill_count,
sum(t.sales_amount) as sales_amount, sum(t.product_amount) as product_amount,
sum(t.sales_gross_profit) as sales_gross_profit,
case when sum(case when t.profit_incomplete = 0 then t.sales_amount else null end) = 0 then null
+38 -13
View File
@@ -42,17 +42,12 @@ BEGIN
SELECT COUNT(*) INTO n FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'ar_bill' AND column_name = 'profit_sales_user_name';
IF n = 0 THEN ALTER TABLE ar_bill ADD COLUMN profit_sales_user_name VARCHAR(64) NULL COMMENT '账单销售员姓名快照'; END IF;
SELECT COUNT(*) INTO n FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'ar_bill' AND column_name = 'profit_snapshot_source';
IF n = 0 THEN ALTER TABLE ar_bill ADD COLUMN profit_snapshot_source VARCHAR(16) NULL COMMENT 'NEW生成时快照/BACKFILLED历史补录'; END IF;
SELECT COUNT(*) INTO n FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'ar_bill'
AND is_nullable = 'YES'
AND ((column_name = 'profit_customer_area' AND data_type = 'varchar' AND character_maximum_length = 50)
OR (column_name = 'profit_sales_user_id' AND data_type = 'bigint')
OR (column_name IN ('profit_sales_user_code','profit_sales_user_name') AND data_type = 'varchar' AND character_maximum_length = 64)
OR (column_name = 'profit_snapshot_source' AND data_type = 'varchar' AND character_maximum_length = 16));
IF n <> 5 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '账单快照字段结构冲突'; END IF;
OR (column_name IN ('profit_sales_user_code','profit_sales_user_name') AND data_type = 'varchar' AND character_maximum_length = 64));
IF n <> 4 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '账单快照字段结构冲突'; END IF;
SELECT COUNT(*) INTO n FROM information_schema.statistics WHERE table_schema = DATABASE()
AND table_name = 'ar_bill' AND index_name = 'idx_ar_profit_sales_date';
IF n = 0 THEN ALTER TABLE ar_bill ADD INDEX idx_ar_profit_sales_date (profit_sales_user_id, bill_date, ar_bill_id); END IF;
@@ -256,7 +251,7 @@ JOIN ctr_customer c ON c.customer_id = b.customer_id
JOIN sys_user u ON CONVERT(CAST(u.user_id AS CHAR) USING utf8mb4) COLLATE utf8mb4_unicode_ci = c.sales_user_id AND u.del_flag = '0'
JOIN ar_bill_detail d ON d.ar_bill_id = b.ar_bill_id
JOIN prc_order_bill p ON p.bill_id = d.prc_order_bill_id
WHERE b.profit_sales_user_id IS NULL AND (b.profit_snapshot_source IS NULL OR b.profit_snapshot_source = 'BACKFILLED')
WHERE b.profit_sales_user_id IS NULL
AND p.belong_saler_code IS NOT NULL
AND TRIM(p.belong_saler_code) <> ''
AND TRIM(p.belong_saler_code) <> u.user_name COLLATE utf8mb4_unicode_ci
@@ -265,7 +260,7 @@ WHERE b.profit_sales_user_id IS NULL AND (b.profit_snapshot_source IS NULL OR b.
-- 此处仅补空值。客户区域来自客户当前资料,并非出账时的历史事实。
UPDATE ar_bill b JOIN ctr_customer c ON c.customer_id = b.customer_id
SET b.profit_customer_area = c.customer_area
WHERE b.profit_customer_area IS NULL AND (b.profit_snapshot_source IS NULL OR b.profit_snapshot_source = 'BACKFILLED')
WHERE b.profit_customer_area IS NULL
AND c.customer_area IS NOT NULL AND TRIM(c.customer_area) <> '';
UPDATE ar_bill b
@@ -274,7 +269,7 @@ JOIN sys_user u ON CONVERT(CAST(u.user_id AS CHAR) USING utf8mb4) COLLATE utf8mb
SET b.profit_sales_user_id = u.user_id,
b.profit_sales_user_code = u.user_name,
b.profit_sales_user_name = u.nick_name
WHERE b.profit_sales_user_id IS NULL AND (b.profit_snapshot_source IS NULL OR b.profit_snapshot_source = 'BACKFILLED')
WHERE b.profit_sales_user_id IS NULL
AND NOT EXISTS (
SELECT 1 FROM ar_bill_detail d JOIN prc_order_bill p ON p.bill_id = d.prc_order_bill_id
WHERE d.ar_bill_id = b.ar_bill_id AND p.belong_saler_code IS NOT NULL
@@ -283,9 +278,6 @@ WHERE b.profit_sales_user_id IS NULL AND (b.profit_snapshot_source IS NULL OR b.
AND TRIM(p.belong_saler_code) <> CONVERT(CAST(u.user_id AS CHAR) USING utf8mb4) COLLATE utf8mb4_unicode_ci
);
UPDATE ar_bill SET profit_snapshot_source = 'BACKFILLED'
WHERE profit_snapshot_source IS NULL AND (profit_customer_area IS NOT NULL OR profit_sales_user_id IS NOT NULL);
-- 历史核销比例不可追溯,不能使用当前比例替代。
INSERT INTO ar_profit_writeoff_rate (writeoff_id, sales_user_id, rate_id, rate, create_time)
SELECT w.writeoff_id, b.profit_sales_user_id, NULL, NULL, NOW()
@@ -337,3 +329,36 @@ END$$
DELIMITER ;
CALL bms_ar_profit_upgrade_20261105_v1();
DROP PROCEDURE IF EXISTS bms_ar_profit_upgrade_20261105_v1;
-- 删除已废弃的来源标记,并把保留的四个快照字段放在 customer_name 后;顺序正确时跳过表变更。
SELECT COUNT(*) INTO @bms_ar_profit_source_column_count
FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'ar_bill' AND column_name = 'profit_snapshot_source';
SELECT COUNT(*) INTO @bms_ar_profit_column_order_count
FROM information_schema.columns c
JOIN information_schema.columns base_column
ON base_column.table_schema = c.table_schema
AND base_column.table_name = c.table_name
AND base_column.column_name = 'customer_name'
WHERE c.table_schema = DATABASE() AND c.table_name = 'ar_bill'
AND ((c.column_name = 'profit_customer_area' AND c.ordinal_position = base_column.ordinal_position + 1)
OR (c.column_name = 'profit_sales_user_id' AND c.ordinal_position = base_column.ordinal_position + 2)
OR (c.column_name = 'profit_sales_user_code' AND c.ordinal_position = base_column.ordinal_position + 3)
OR (c.column_name = 'profit_sales_user_name' AND c.ordinal_position = base_column.ordinal_position + 4));
SET @bms_ar_profit_snapshot_column_definitions =
'MODIFY COLUMN profit_customer_area VARCHAR(50) COLLATE utf8mb4_unicode_ci NULL COMMENT ''账单客户区域快照'' AFTER customer_name,
MODIFY COLUMN profit_sales_user_id BIGINT(20) NULL COMMENT ''账单销售员用户ID快照'' AFTER profit_customer_area,
MODIFY COLUMN profit_sales_user_code VARCHAR(64) COLLATE utf8mb4_unicode_ci NULL COMMENT ''账单销售员工号快照'' AFTER profit_sales_user_id,
MODIFY COLUMN profit_sales_user_name VARCHAR(64) COLLATE utf8mb4_unicode_ci NULL COMMENT ''账单销售员姓名快照'' AFTER profit_sales_user_code';
SET @bms_ar_profit_column_order_sql = CASE
WHEN @bms_ar_profit_source_column_count > 0 AND @bms_ar_profit_column_order_count = 4
THEN 'ALTER TABLE ar_bill DROP COLUMN profit_snapshot_source'
WHEN @bms_ar_profit_source_column_count > 0
THEN CONCAT('ALTER TABLE ar_bill DROP COLUMN profit_snapshot_source, ', @bms_ar_profit_snapshot_column_definitions)
WHEN @bms_ar_profit_column_order_count <> 4
THEN CONCAT('ALTER TABLE ar_bill ', @bms_ar_profit_snapshot_column_definitions)
ELSE 'SELECT 1 WHERE 1 = 0'
END;
PREPARE bms_ar_profit_column_order_stmt FROM @bms_ar_profit_column_order_sql;
EXECUTE bms_ar_profit_column_order_stmt;
DEALLOCATE PREPARE bms_ar_profit_column_order_stmt;