src/main/resources/mapper/basic/CustomerMapper.xml
@@ -9,7 +9,7 @@
    <select id="listPage" resultType="com.ruoyi.basic.vo.CustomerVo">
        select
        c.*,
        u.user_name usage_user_name,
        u.nick_name usage_user_name,
        (
        select group_concat(u2.user_name separator ', ')
        from customer_user cu
@@ -26,7 +26,6 @@
        from customer c
        left join sys_user u on c.usage_user = u.user_id
        <where>
            and c.usage_status = 1
            <if test="c.customerName != null and c.customerName != ''">
                and customer_name like concat('%', #{c.customerName}, '%')
            </if>
@@ -53,6 +52,7 @@
                )
            </if>
        </where>
        order by c.id desc
    </select>
    <select id="list" resultType="com.ruoyi.basic.vo.CustomerVo">
@@ -107,5 +107,163 @@
                )
            </if>
        </where>
        order by c.id desc
    </select>
    <!--
      客户往来(回款台账主表):
        应收金额 = 各销售台账的应收金额之和,
        单条台账应收金额 = 该台账已出库金额 − 已退货款 − 已回款金额(不小于 0)。
      即「回款金额」是累计回款,「应收金额」是还没收回来的部分。
      注意 出库金额 = sum(出库数量 * 6位小数的含税单价),会带有「分」以下的零头,
           而回款是按分录入的整数金额,两者相减会出现 0.0045、0.004 这类无意义的尾差,
           因此单条台账的应收金额按分计算,分以下的尾差不计入。
      注意 必须先按台账算好应收、再按客户汇总(与 customewTransactionsDetails 口径一致)。
           若先按客户汇总再取整,各家台账的尾差会累加,出现「主表 0.01、明细全是 0」的对不上问题。
      注意 ★ 出库金额按「销售明细的含税总额」折算,不能直接用「出库数量 × 含税单价」:
              出库金额 = Σ ROUND( 该明细含税总额 × LEAST(1, 该明细已出库量 / 该明细销售数量), 2 )
            原因:销售数量可能带 3 位小数(如 47.328),而出库数量按手册 §5.6 只保留 2 位(47.33)。
            直接相乘得 47.33 × 172 = 8140.76,而开票金额是 47.328 × 172 = 8140.42,
            凭空多出 0.34 的应收 —— 实测黑龙江北盛壶关分公司 13370.02 已全额回款,
            页面上却仍显示欠 0.34。
            · LEAST(1, ...) 用来封顶:出库量 ≥ 销售数量时最多算整条明细的开票金额,超发不增加应收;
              出库量 < 销售数量(部分发货)时按比例计。
            · 每条明细单独 ROUND 到分,避免 0.095 这类分以下零头累积成应收。
      注意 出库金额按台账 (sales_ledger.id) 聚合,同一发货单多批号出库时不会重复计数。
    -->
    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
        select T1.customer_id,
               c.customer_name,
               T1.contractAmounts,
               IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
               IFNULL(T5.receiptableAmount, 0) AS receiptableAmount
        from (select customer_id, sum(contract_amount) as contractAmounts from sales_ledger group by customer_id) T1
        left join (select customer_id, sum(collection_amount) as receiptPaymentAmount from account_sales_collection group by customer_id) T2 on T1.customer_id = T2.customer_id
        left join (
            select
                sl.customer_id,
                sum(GREATEST(
                    TRUNCATE(IFNULL(T3.outboundAmount, 0) - IFNULL(T4.returnAmount, 0)
                                 - IFNULL(T6.receiptPaymentAmount, 0), 2),
                    0)) AS receiptableAmount
            from sales_ledger sl
            left join (
                select
                    sl.id,
                    sum(t.collection_amount) as receiptPaymentAmount
                from (
                    select
                        ascc.collection_amount,
                        s.sales_ledger_id
                    from account_sales_collection ascc
                    left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
                    left join shipping_info s on sor.record_id = s.id
                    where sor.record_type='13'
                      and sor.approval_status=1
                    group by ascc.id, s.sales_ledger_id
                ) t
                left join sales_ledger sl on t.sales_ledger_id = sl.id
                group by sl.id
            ) T6 on T6.id = sl.id
            left join (
                SELECT
                    sl.id,
                    sum(ROUND(slp.tax_inclusive_total_price
                              * LEAST(1, ship.out_qty / NULLIF(slp.quantity, 0)), 2)) as outboundAmount
                FROM (
                    SELECT sor.record_id AS shipping_id, SUM(sor.stock_out_num) AS out_qty
                    FROM stock_out_record sor
                    WHERE sor.record_type='13' AND sor.approval_status=1
                    GROUP BY sor.record_id
                ) ship
                JOIN shipping_info s ON s.id = ship.shipping_id
                LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
                LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id
                WHERE slp.type = 1
                group by sl.id
            ) T3 on T3.id = sl.id
            left join (
                select
                    sl.id,
                    sum(rm.refund_amount) as returnAmount
                from return_management rm
                left join shipping_info si on rm.shipping_id = si.id
                left join sales_ledger sl on si.sales_ledger_id = sl.id
                where rm.status=1
                group by sl.id
            ) T4 on T4.id = sl.id
            group by sl.customer_id
        ) T5 on T5.customer_id = T1.customer_id
        left join customer c on T1.customer_id = c.id
        <where>
            <if test="customerName!=null and customerName!=''">
                AND c.customer_name LIKE CONCAT('%', #{customerName}, '%')
            </if>
        </where>
        order by T1.customer_id desc
    </select>
    <!--
      客户往来明细(按销售台账):应收金额 = 该台账已出库 − 已退货 − 已回款。
      单条回款只统计一次:同一回款可能同时命中该台账的多条出库记录,
      先用 GROUP BY ascc.id, sl.id 去重,再按台账求和。
      金额同样按分(2 位小数)计算,分以下的尾差不计入。
    -->
    <select id="customewTransactionsDetails"
            resultType="com.ruoyi.sales.vo.CustomerTransactionsDetailsVo">
        select sl.id salesLedgerId,
               sl.sales_contract_no,
               sl.execution_date,
               sl.contract_amount,
               IFNULL(T1.receiptPaymentAmount, 0) AS receiptPaymentAmount,
               GREATEST(
                   TRUNCATE(IFNULL(T2.outboundAmount, 0) - IFNULL(T3.returnAmount, 0)
                                - IFNULL(T1.receiptPaymentAmount, 0), 2),
                   0) AS receiptableAmount
        from sales_ledger sl
        left join (
            select
                sl.id,
                sum(t.collection_amount) as receiptPaymentAmount
            from (
                select
                    ascc.collection_amount,
                    s.sales_ledger_id
                from account_sales_collection ascc
                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
                left join shipping_info s on sor.record_id = s.id
                where sor.record_type='13'
                  and sor.approval_status=1
                group by ascc.id, s.sales_ledger_id
            ) t
            left join sales_ledger sl on t.sales_ledger_id = sl.id
            group by sl.id
        )T1 on T1.id = sl.id
        left join (
            SELECT
                sl.id,
                sum(ROUND(slp.tax_inclusive_total_price
                          * LEAST(1, ship.out_qty / NULLIF(slp.quantity, 0)), 2)) as outboundAmount
            FROM (
                SELECT sor.record_id AS shipping_id, SUM(sor.stock_out_num) AS out_qty
                FROM stock_out_record sor
                WHERE sor.record_type='13' AND sor.approval_status=1
                GROUP BY sor.record_id
            ) ship
            JOIN shipping_info s ON s.id = ship.shipping_id
            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
            LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id
            WHERE slp.type = 1
            group by  sl.id
        )T2 on T2.id = sl.id
        left join (
            select sl.id,
                   sum(rm.refund_amount) as returnAmount
            from return_management rm
                     left join shipping_info si on rm.shipping_id = si.id
                     left join sales_ledger sl on si.sales_ledger_id = sl.id
            where rm.status=1
            group by sl.id
        )T3 on T3.id = sl.id
        where sl.customer_id = #{customerId}
        order by sl.id desc
    </select>
</mapper>