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;
|
package com.bms.report;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -11,18 +13,93 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.jupiter.api.Test;
|
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.ArBalanceWriteoff;
|
||||||
import com.bms.receivable.domain.ArBill;
|
import com.bms.receivable.domain.ArBill;
|
||||||
|
import com.bms.receivable.mapper.ArBillMapper;
|
||||||
import com.bms.receivable.mapper.ArCommissionMapper;
|
import com.bms.receivable.mapper.ArCommissionMapper;
|
||||||
import com.bms.receivable.profit.ArCommissionRate;
|
import com.bms.receivable.profit.ArCommissionRate;
|
||||||
import com.bms.receivable.profit.ArCommissionResult;
|
import com.bms.receivable.profit.ArCommissionResult;
|
||||||
import com.bms.receivable.profit.ArCommissionService;
|
import com.bms.receivable.profit.ArCommissionService;
|
||||||
import com.bms.receivable.profit.ArProfitScope;
|
import com.bms.receivable.profit.ArProfitScope;
|
||||||
import com.bms.receivable.profit.ArProfitScopeResolver;
|
import com.bms.receivable.profit.ArProfitScopeResolver;
|
||||||
|
import com.bms.receivable.profit.ArWriteoffRate;
|
||||||
import com.bms.common.exception.ServiceException;
|
import com.bms.common.exception.ServiceException;
|
||||||
|
|
||||||
class ArCommissionServiceTest
|
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
|
@Test
|
||||||
void commissionReadsUseServerResolvedScopeAndManagementRemainsRestricted()
|
void commissionReadsUseServerResolvedScopeAndManagementRemainsRestricted()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -158,6 +158,17 @@ class ArProfitMapperSqlTest
|
|||||||
.getBoundSql(Map.of("billId", 1L)).getSql();
|
.getBoundSql(Map.of("billId", 1L)).getSql();
|
||||||
assertTrue(settlement.contains("w.status = 'EFFECTIVE'"));
|
assertTrue(settlement.contains("w.status = 'EFFECTIVE'"));
|
||||||
assertTrue(settlement.contains("b.status = 'WRITTEN_OFF'"));
|
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")
|
var insert = configuration.getMappedStatement("com.bms.receivable.mapper.ArCommissionMapper.insertResults")
|
||||||
.getBoundSql(Map.of("items", java.util.List.of(new ArCommissionResult(), new ArCommissionResult()), "operator", "test"));
|
.getBoundSql(Map.of("items", java.util.List.of(new ArCommissionResult(), new ArCommissionResult()), "operator", "test"));
|
||||||
assertTrue(insert.getSql().contains("ar_profit_commission_result"));
|
assertTrue(insert.getSql().contains("ar_profit_commission_result"));
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public interface ArCommissionMapper
|
|||||||
List<ArWriteoffRate> selectMissingRates();
|
List<ArWriteoffRate> selectMissingRates();
|
||||||
List<ArWriteoffRate> selectWriteoffRates(@Param("billId") Long billId);
|
List<ArWriteoffRate> selectWriteoffRates(@Param("billId") Long billId);
|
||||||
Long selectBillIdByWriteoffId(@Param("writeoffId") Long writeoffId);
|
Long selectBillIdByWriteoffId(@Param("writeoffId") Long writeoffId);
|
||||||
|
Integer isBillProfitDataComplete(@Param("billId") Long billId);
|
||||||
int countMissingRates(@Param("billId") Long billId);
|
int countMissingRates(@Param("billId") Long billId);
|
||||||
int confirmMissingRate(@Param("writeoffId") Long writeoffId, @Param("rate") java.math.BigDecimal rate,
|
int confirmMissingRate(@Param("writeoffId") Long writeoffId, @Param("rate") java.math.BigDecimal rate,
|
||||||
@Param("operator") String operator, @Param("remark") String remark);
|
@Param("operator") String operator, @Param("remark") String remark);
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ public class ArCommissionService
|
|||||||
|| ArBillStatus.PARTIAL_WRITTEN_OFF.getCode().equals(bill.getStatus())
|
|| ArBillStatus.PARTIAL_WRITTEN_OFF.getCode().equals(bill.getStatus())
|
||||||
|| ArBillStatus.WRITTEN_OFF.getCode().equals(bill.getStatus())))
|
|| ArBillStatus.WRITTEN_OFF.getCode().equals(bill.getStatus())))
|
||||||
throw new ServiceException("账单未生成完成或状态不允许确认提成比例");
|
throw new ServiceException("账单未生成完成或状态不允许确认提成比例");
|
||||||
|
if (!Integer.valueOf(1).equals(mapper.isBillProfitDataComplete(billId)))
|
||||||
|
throw new ServiceException("账单价格、金额或币种数据不完整或不一致,请先处理后再确认比例");
|
||||||
String operator = SecurityUtils.getUsername();
|
String operator = SecurityUtils.getUsername();
|
||||||
if (mapper.confirmMissingRate(input.getWriteoffId(), input.getRate(), operator, input.getRemark().trim()) != 1)
|
if (mapper.confirmMissingRate(input.getWriteoffId(), input.getRate(), operator, input.getRemark().trim()) != 1)
|
||||||
throw new ServiceException("比例已确认或核销状态已变化,请刷新");
|
throw new ServiceException("比例已确认或核销状态已变化,请刷新");
|
||||||
|
|||||||
@@ -79,6 +79,41 @@
|
|||||||
<select id="selectBillIdByWriteoffId" resultType="long">
|
<select id="selectBillIdByWriteoffId" resultType="long">
|
||||||
select ar_bill_id from ar_balance_writeoff where writeoff_id = #{writeoffId} and status = 'EFFECTIVE'
|
select ar_bill_id from ar_balance_writeoff where writeoff_id = #{writeoffId} and status = 'EFFECTIVE'
|
||||||
</select>
|
</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 id="countMissingRates" resultType="int">
|
||||||
select count(*) from ar_balance_writeoff w
|
select count(*) from ar_balance_writeoff w
|
||||||
left join ar_profit_writeoff_rate r on r.writeoff_id = w.writeoff_id
|
left join ar_profit_writeoff_rate r on r.writeoff_id = w.writeoff_id
|
||||||
@@ -157,14 +192,7 @@
|
|||||||
else null end as issue
|
else null end as issue
|
||||||
from ar_bill b inner join ar_bill_detail d on d.ar_bill_id = b.ar_bill_id
|
from ar_bill b inner join ar_bill_detail d on d.ar_bill_id = b.ar_bill_id
|
||||||
left join (
|
left join (
|
||||||
select fee.detail_id, case when count(fee.amount) = count(*) then sum(fee.amount) else null end as sales_amount,
|
<include refid="feeSnapshotAggregation"/>
|
||||||
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
|
|
||||||
) f on f.detail_id = d.detail_id
|
) f on f.detail_id = d.detail_id
|
||||||
left join (
|
left join (
|
||||||
select w.ar_bill_id as bill_id, sum(w.writeoff_amount) as written_amount,
|
select w.ar_bill_id as bill_id, sum(w.writeoff_amount) as written_amount,
|
||||||
|
|||||||
Reference in New Issue
Block a user