Merge branch 'test' of https://git.lionquexpress.com/wuweihua/BMS into test
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package com.bms.report;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigDecimal;
|
||||
@@ -11,18 +13,93 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.core.domain.model.LoginUser;
|
||||
import com.bms.receivable.domain.ArBalanceWriteoff;
|
||||
import com.bms.receivable.domain.ArBill;
|
||||
import com.bms.receivable.mapper.ArBillMapper;
|
||||
import com.bms.receivable.mapper.ArCommissionMapper;
|
||||
import com.bms.receivable.profit.ArCommissionRate;
|
||||
import com.bms.receivable.profit.ArCommissionResult;
|
||||
import com.bms.receivable.profit.ArCommissionService;
|
||||
import com.bms.receivable.profit.ArProfitScope;
|
||||
import com.bms.receivable.profit.ArProfitScopeResolver;
|
||||
import com.bms.receivable.profit.ArWriteoffRate;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
|
||||
class ArCommissionServiceTest
|
||||
{
|
||||
@Test
|
||||
void rateConfirmationRequiresCompleteProfitDataBeforeAnyWrite()
|
||||
{
|
||||
List<String> calls = new ArrayList<>();
|
||||
AtomicReference<Integer> complete = new AtomicReference<>(0);
|
||||
ArCommissionMapper mapper = (ArCommissionMapper) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { ArCommissionMapper.class }, (proxy, method, args) -> {
|
||||
calls.add(method.getName());
|
||||
return switch (method.getName()) {
|
||||
case "selectBillIdByWriteoffId" -> 25L;
|
||||
case "isBillProfitDataComplete" -> complete.get();
|
||||
case "confirmMissingRate", "reverseResults" -> 1;
|
||||
case "countMissingRates" -> 0;
|
||||
case "selectSettlementInputs" -> List.of();
|
||||
default -> throw new AssertionError(method.getName());
|
||||
};
|
||||
});
|
||||
ArBill bill = new ArBill();
|
||||
bill.setProfitSalesUserId(7L);
|
||||
bill.setGenerationStatus("READY");
|
||||
ArBillMapper billMapper = (ArBillMapper) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { ArBillMapper.class }, (proxy, method, args) -> {
|
||||
if ("selectArBillByIdForUpdate".equals(method.getName())) return bill;
|
||||
throw new AssertionError(method.getName());
|
||||
});
|
||||
ArProfitScopeResolver resolver = new ArProfitScopeResolver() {
|
||||
@Override public ArProfitScope resolve() { return new ArProfitScope(true, false, false, 1L, 1L); }
|
||||
};
|
||||
ArCommissionService service = new ArCommissionService(mapper, resolver, null, billMapper);
|
||||
ArWriteoffRate input = new ArWriteoffRate();
|
||||
input.setWriteoffId(18L);
|
||||
input.setRate(new BigDecimal("5"));
|
||||
input.setRemark("已核对");
|
||||
for (String status : List.of("CONFIRMED", "PARTIAL_WRITTEN_OFF", "WRITTEN_OFF"))
|
||||
{
|
||||
bill.setStatus(status);
|
||||
calls.clear();
|
||||
ServiceException error = assertThrows(ServiceException.class, () -> service.confirmMissingRate(input));
|
||||
assertTrue(error.getMessage().contains("请先处理后再确认比例"));
|
||||
assertFalse(calls.contains("confirmMissingRate"));
|
||||
assertFalse(calls.contains("reverseResults"));
|
||||
}
|
||||
complete.set(null);
|
||||
assertThrows(ServiceException.class, () -> service.confirmMissingRate(input));
|
||||
complete.set(1);
|
||||
SysUser user = new SysUser();
|
||||
user.setUserName("finance");
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(new LoginUser(user, java.util.Set.of()), null));
|
||||
try
|
||||
{
|
||||
bill.setStatus("PARTIAL_WRITTEN_OFF");
|
||||
calls.clear();
|
||||
service.confirmMissingRate(input);
|
||||
assertTrue(calls.contains("confirmMissingRate"));
|
||||
assertFalse(calls.contains("reverseResults"));
|
||||
bill.setStatus("WRITTEN_OFF");
|
||||
calls.clear();
|
||||
service.confirmMissingRate(input);
|
||||
assertTrue(calls.indexOf("isBillProfitDataComplete") < calls.indexOf("confirmMissingRate"));
|
||||
assertTrue(calls.contains("reverseResults"));
|
||||
assertTrue(calls.contains("selectSettlementInputs"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void commissionReadsUseServerResolvedScopeAndManagementRemainsRestricted()
|
||||
{
|
||||
|
||||
@@ -158,6 +158,17 @@ class ArProfitMapperSqlTest
|
||||
.getBoundSql(Map.of("billId", 1L)).getSql();
|
||||
assertTrue(settlement.contains("w.status = 'EFFECTIVE'"));
|
||||
assertTrue(settlement.contains("b.status = 'WRITTEN_OFF'"));
|
||||
String validation = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.isBillProfitDataComplete")
|
||||
.getBoundSql(Map.of("billId", 1L)).getSql();
|
||||
assertTrue(validation.contains("detail.ar_bill_id = ?"));
|
||||
assertTrue(validation.contains("d.ar_bill_id = ?"));
|
||||
assertTrue(validation.contains("a.detail_total = b.total_amount"));
|
||||
assertTrue(validation.contains("f.sales_amount = d.total_amount"));
|
||||
assertTrue(validation.contains("f.fee_currency = d.currency"));
|
||||
assertTrue(validation.contains("f.product_complete = 1"));
|
||||
assertTrue(validation.contains("f.redline_complete = 1"));
|
||||
assertFalse(validation.contains("b.status = 'WRITTEN_OFF'"));
|
||||
assertFalse(validation.contains("profit_calc_status"));
|
||||
var insert = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.insertResults")
|
||||
.getBoundSql(Map.of("items", java.util.List.of(new ArCommissionResult(), new ArCommissionResult()), "operator", "test"));
|
||||
assertTrue(insert.getSql().contains("ar_profit_commission_result"));
|
||||
|
||||
@@ -23,6 +23,7 @@ public interface ArCommissionMapper
|
||||
List<ArWriteoffRate> selectMissingRates();
|
||||
List<ArWriteoffRate> selectWriteoffRates(@Param("billId") Long billId);
|
||||
Long selectBillIdByWriteoffId(@Param("writeoffId") Long writeoffId);
|
||||
Integer isBillProfitDataComplete(@Param("billId") Long billId);
|
||||
int countMissingRates(@Param("billId") Long billId);
|
||||
int confirmMissingRate(@Param("writeoffId") Long writeoffId, @Param("rate") java.math.BigDecimal rate,
|
||||
@Param("operator") String operator, @Param("remark") String remark);
|
||||
|
||||
@@ -134,6 +134,8 @@ public class ArCommissionService
|
||||
|| ArBillStatus.PARTIAL_WRITTEN_OFF.getCode().equals(bill.getStatus())
|
||||
|| ArBillStatus.WRITTEN_OFF.getCode().equals(bill.getStatus())))
|
||||
throw new ServiceException("账单未生成完成或状态不允许确认提成比例");
|
||||
if (!Integer.valueOf(1).equals(mapper.isBillProfitDataComplete(billId)))
|
||||
throw new ServiceException("账单价格、金额或币种数据不完整或不一致,请先处理后再确认比例");
|
||||
String operator = SecurityUtils.getUsername();
|
||||
if (mapper.confirmMissingRate(input.getWriteoffId(), input.getRate(), operator, input.getRemark().trim()) != 1)
|
||||
throw new ServiceException("比例已确认或核销状态已变化,请刷新");
|
||||
|
||||
@@ -79,6 +79,41 @@
|
||||
<select id="selectBillIdByWriteoffId" resultType="long">
|
||||
select ar_bill_id from ar_balance_writeoff where writeoff_id = #{writeoffId} and status = 'EFFECTIVE'
|
||||
</select>
|
||||
<!-- 供比例确认和正式提成共用费用快照口径。 -->
|
||||
<sql id="feeSnapshotAggregation">
|
||||
select fee.detail_id, case when count(fee.amount) = count(*) then sum(fee.amount) else null end as sales_amount,
|
||||
case when count(fee.currency) = count(*) then count(distinct fee.currency) else 0 end as currency_count,
|
||||
min(fee.currency) as fee_currency,
|
||||
case when count(fee.product_amount) = count(*) then sum(fee.product_amount) else null end as product_amount,
|
||||
case when count(fee.redline_amount) = count(*) then sum(fee.redline_amount) else null end as redline_amount,
|
||||
case when count(fee.product_amount) = count(*) then 1 else 0 end as product_complete,
|
||||
case when count(fee.redline_amount) = count(*) then 1 else 0 end as redline_complete
|
||||
from ar_bill_detail_fee fee join ar_bill_detail detail on detail.detail_id = fee.detail_id
|
||||
where detail.ar_bill_id = #{billId} group by fee.detail_id
|
||||
</sql>
|
||||
<!-- 不依赖核销完成状态或账单汇总快照,确认/部分核销/已核销账单均检查明细。 -->
|
||||
<select id="isBillProfitDataComplete" resultType="int">
|
||||
<bind name="shenzhenArea" value="@com.bms.price.enums.ProfitAreaRule@SHENZHEN.getCustomerArea()"/>
|
||||
<bind name="guangzhouArea" value="@com.bms.price.enums.ProfitAreaRule@GUANGZHOU.getCustomerArea()"/>
|
||||
select case when b.total_amount is not null and b.total_amount > 0
|
||||
and a.detail_count > 0 and a.invalid_count = 0 and a.detail_total = b.total_amount
|
||||
then 1 else 0 end
|
||||
from ar_bill b
|
||||
left join (
|
||||
select d.ar_bill_id, count(*) as detail_count, sum(d.total_amount) as detail_total,
|
||||
sum(case when d.total_amount is not null and d.currency is not null and b.currency is not null
|
||||
and d.currency = b.currency and f.currency_count = 1 and f.fee_currency = d.currency
|
||||
and f.sales_amount is not null and f.sales_amount = d.total_amount
|
||||
and ((b.profit_customer_area = #{shenzhenArea} and f.product_complete = 1)
|
||||
or (b.profit_customer_area = #{guangzhouArea} and f.redline_complete = 1))
|
||||
then 0 else 1 end) as invalid_count
|
||||
from ar_bill_detail d join ar_bill b on b.ar_bill_id = d.ar_bill_id
|
||||
left join (<include refid="feeSnapshotAggregation"/>) f on f.detail_id = d.detail_id
|
||||
where d.ar_bill_id = #{billId}
|
||||
group by d.ar_bill_id
|
||||
) a on a.ar_bill_id = b.ar_bill_id
|
||||
where b.ar_bill_id = #{billId}
|
||||
</select>
|
||||
<select id="countMissingRates" resultType="int">
|
||||
select count(*) from ar_balance_writeoff w
|
||||
left join ar_profit_writeoff_rate r on r.writeoff_id = w.writeoff_id
|
||||
@@ -157,14 +192,7 @@
|
||||
else null end as issue
|
||||
from ar_bill b inner join ar_bill_detail d on d.ar_bill_id = b.ar_bill_id
|
||||
left join (
|
||||
select fee.detail_id, case when count(fee.amount) = count(*) then sum(fee.amount) else null end as sales_amount,
|
||||
case when count(fee.currency) = count(*) then count(distinct fee.currency) else 0 end as currency_count, min(fee.currency) as fee_currency,
|
||||
case when count(fee.product_amount) = count(*) then sum(fee.product_amount) else null end as product_amount,
|
||||
case when count(fee.redline_amount) = count(*) then sum(fee.redline_amount) else null end as redline_amount,
|
||||
case when count(fee.product_amount) = count(*) then 1 else 0 end as product_complete,
|
||||
case when count(fee.redline_amount) = count(*) then 1 else 0 end as redline_complete
|
||||
from ar_bill_detail_fee fee join ar_bill_detail detail on detail.detail_id = fee.detail_id
|
||||
where detail.ar_bill_id = #{billId} group by fee.detail_id
|
||||
<include refid="feeSnapshotAggregation"/>
|
||||
) f on f.detail_id = d.detail_id
|
||||
left join (
|
||||
select w.ar_bill_id as bill_id, sum(w.writeoff_amount) as written_amount,
|
||||
|
||||
Reference in New Issue
Block a user